001/* 002 * Copyright (c) 2012-2021 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package gololang; 012 013import java.util.Iterator; 014import org.eclipse.golo.runtime.InvalidDestructuringException; 015 016/** 017 * Base class for Golo structure objects. 018 * <p> 019 * This class defines common behavior. Golo structure classes are final subclasses of this one. 020 */ 021public abstract class GoloStruct implements Iterable<Tuple>, Comparable<GoloStruct> { 022 023 /** 024 * The array of member names, initialized in Golo structure classes constructors. 025 */ 026 protected String[] members; 027 028 /** 029 * Constructor that does nothing beyond calling {@code super()}. 030 */ 031 public GoloStruct() { 032 super(); 033 } 034 035 /** 036 * Tells whether the instance is frozen or not. 037 * 038 * @return {@code true} if frozen, {@code false} otherwise. 039 */ 040 public abstract boolean isFrozen(); 041 042 /** 043 * Gets the member names as a tuple of strings. 044 * 045 * @return a tuple of member names. 046 */ 047 public Tuple members() { 048 return Tuple.fromArray(members); 049 } 050 051 /** 052 * Gets the current values, in order of member declaration. 053 * 054 * @return a tuple with the current values. 055 */ 056 public Tuple values() { 057 return Tuple.fromArray(toArray()); 058 } 059 060 /** 061 * Destructuration helper. 062 * 063 * @return a tuple with the current values. 064 * @deprecated This method should not be called directly and is no more used by new style destructuring. 065 */ 066 @Deprecated 067 public Tuple destruct() { 068 return Tuple.fromArray(toArray()); 069 } 070 071 /** 072 * New style destructuring helper. 073 * 074 * <p>The number of variables to be affected must be the number of members. 075 * No remainer syntax is allowed. 076 * 077 * @param number number of variable that will be affected. 078 * @param substruct whether the destructuring is complete or should contains a sub structure. 079 * @param toSkip a boolean array indicating the elements to skip. 080 * @return an array containing the values to assign. 081 */ 082 public Object[] __$$_destruct(int number, boolean substruct, Object[] toSkip) { 083 if (number == this.members.length && !substruct) { 084 return toArray(); 085 } 086 if (number <= this.members.length) { 087 throw InvalidDestructuringException.notEnoughValues(number, this.members.length, substruct); 088 } 089 throw InvalidDestructuringException.tooManyValues(number); 090 } 091 092 /** 093 * Array conversion. 094 * 095 * @return an array containing the values (in member orders) 096 */ 097 public abstract Object[] toArray(); 098 099 /** 100 * Compares this structure with the specified structure for order. 101 * <p>Returns a negative integer, zero, or a positive integer as this structure is less than, 102 * equal to, or greater than the specified structure. 103 * <p>Two structures are compared by comparing their {@link #values()}, thus the 104 * limitations of {@link gololang.Tuple#compareTo} also apply. 105 * <p>Moreover, two structures are only comparable if they have the same type. For instance, 106 * given 107 * <pre class="lisgin"><code class="lang-golo" data-lang="golo"> 108 * struct StructA = {x, y} 109 * struct StructB = {a, b} 110 * 111 * let aStructA = StructA(1, 2) 112 * let aStructB = StructB(1, 3) 113 * </code></pre> 114 * while {@code aStructA: values() < aStructB: values()} is valid and true since we compare two 115 * 2-tuples, comparing directly the structures {@code aStructA < aStructB} throws a 116 * {@link java.lang.ClassCastException}. 117 * 118 * @param other the structure to be compared 119 * @return a negative integer, zero, or a positive integer as this structure is less than, equal to, or greater than the specified structure 120 * @throws NullPointerException if the specified structure is null 121 * @throws IllegalArgumentException if the structure are of different type, of if the type of the members prevent them from being compared pairwise 122 * @since Golo3.1 123 */ 124 @Override 125 public int compareTo(GoloStruct other) { 126 if (this.equals(other)) { 127 return 0; 128 } 129 if (getClass() != other.getClass()) { 130 throw new IllegalArgumentException(String.format( 131 "%s and %s can't be compared; try to compare their values", this, other)); 132 } 133 return this.values().compareTo(other.values()); 134 } 135 136 /** 137 * Gets a member value by name. 138 * 139 * @param member the member name. 140 * @return the member value. 141 * @throws IllegalArgumentException if there is no such member {@code member}. 142 */ 143 public abstract Object get(String member); 144 145 /** 146 * Sets a member value by name. 147 * 148 * @param member the member name. 149 * @param value the value. 150 * @return this instance. 151 * @throws IllegalArgumentException if there is no such member {@code member}. 152 */ 153 public abstract GoloStruct set(String member, Object value); 154 155 /** 156 * Makes a shallow copy. 157 * 158 * @return a copy of this structure. 159 */ 160 public abstract GoloStruct copy(); 161 162 /** 163 * Makes a shallow frozen copy where any member value modification attempt will fail with an {@link IllegalStateException}. 164 * 165 * @return a copy of this structure. 166 */ 167 public abstract GoloStruct frozenCopy(); 168 169 /** 170 * Provides an iterator over the structure. 171 * <p> 172 * Each value is a 2-elements tuple {@code [member, value]}. 173 * 174 * @return an iterator. 175 */ 176 @Override 177 public Iterator<Tuple> iterator() { 178 return new Iterator<Tuple>() { 179 180 final Iterator<?> memberIterator = members().iterator(); 181 final Iterator<?> valuesIterator = values().iterator(); 182 183 @Override 184 public boolean hasNext() { 185 return memberIterator.hasNext(); 186 } 187 188 @Override 189 public Tuple next() { 190 return new Tuple(memberIterator.next(), valuesIterator.next()); 191 } 192 193 @Override 194 public void remove() { 195 throw new UnsupportedOperationException(); 196 } 197 }; 198 } 199}