001/*
002 * Copyright (c) 2012-2018 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;
014
015/**
016 * Base class for Golo structure objects.
017 * <p>
018 * This class defines common behavior. Golo structure classes are final subclasses of this one.
019 */
020public abstract class GoloStruct implements Iterable<Tuple>, Comparable<GoloStruct>  {
021
022  /**
023   * The array of member names, initialized in Golo structure classes constructors.
024   */
025  protected String[] members;
026
027  /**
028   * Constructor that does nothing beyond calling {@code super()}.
029   */
030  public GoloStruct() {
031    super();
032  }
033
034  /**
035   * Tells whether the instance is frozen or not.
036   *
037   * @return {@code true} if frozen, {@code false} otherwise.
038   */
039  public abstract boolean isFrozen();
040
041  /**
042   * Gets the member names as a tuple of strings.
043   *
044   * @return a tuple of member names.
045   */
046  public Tuple members() {
047    return Tuple.fromArray(members);
048  }
049
050  /**
051   * Gets the current values, in order of member declaration.
052   *
053   * @return a tuple with the current values.
054   */
055  public Tuple values() {
056    return Tuple.fromArray(toArray());
057  }
058
059  /**
060   * Destructuration helper.
061   *
062   * @return a tuple with the current values.
063   */
064  public Tuple destruct() {
065    return Tuple.fromArray(toArray());
066  }
067
068  /**
069   * Array conversion.
070   *
071   * @return an array containing the values (in member orders)
072   */
073  public abstract Object[] toArray();
074
075  /**
076   * Compares this structure with the specified structure for order.
077   * <p>Returns a negative integer, zero, or a positive integer as this structure is less than,
078   * equal to, or greater than the specified structure.
079   * <p>Two structures are compared by comparing their {@link #values()}, thus the
080   * limitations of {@link gololang.Tuple#compareTo} also apply.
081   * <p>Moreover, two structures are only comparable if they have the same type. For instance,
082   * given
083   * <pre class="lisgin"><code class="lang-golo" data-lang="golo">
084   * struct StructA = {x, y}
085   * struct StructB = {a, b}
086   *
087   * let aStructA = StructA(1, 2)
088   * let aStructB = StructB(1, 3)
089   * </code></pre>
090   * while {@code aStructA: values() < aStructB: values()} is valid and true since we compare two
091   * 2-tuples, comparing directly the structures {@code aStructA < aStructB} throws a
092   * {@link java.lang.ClassCastException}.
093   *
094   * @param other the structure to be compared
095   * @return a negative integer, zero, or a positive integer as this structure is less than, equal to, or greater than the specified structure
096   * @throws NullPointerException if the specified structure is null
097   * @throws IllegalArgumentException  if the structure are of different type, of if the type of the members prevent them from being compared pairwise
098   * @since Golo3.1
099   */
100  @Override
101  public int compareTo(GoloStruct other) {
102    if (this.equals(other)) {
103      return 0;
104    }
105    if (getClass() != other.getClass()) {
106      throw new IllegalArgumentException(String.format(
107            "%s and %s can't be compared; try to compare their values", this, other));
108    }
109    return this.values().compareTo(other.values());
110  }
111
112  /**
113   * Gets a member value by name.
114   *
115   * @param member the member name.
116   * @return the member value.
117   * @throws IllegalArgumentException if there is no such member {@code member}.
118   */
119  public abstract Object get(String member);
120
121  /**
122   * Sets a member value by name.
123   *
124   * @param member the member name.
125   * @param value  the value.
126   * @return this instance.
127   * @throws IllegalArgumentException if there is no such member {@code member}.
128   */
129  public abstract GoloStruct set(String member, Object value);
130
131  /**
132   * Makes a shallow copy.
133   *
134   * @return a copy of this structure.
135   */
136  public abstract GoloStruct copy();
137
138  /**
139   * Makes a shallow frozen copy where any member value modification attempt will fail with an {@link IllegalStateException}.
140   *
141   * @return a copy of this structure.
142   */
143  public abstract GoloStruct frozenCopy();
144
145  /**
146   * Provides an iterator over the structure.
147   * <p>
148   * Each value is a 2-elements tuple {@code [member, value]}.
149   *
150   * @return an iterator.
151   */
152  @Override
153  public Iterator<Tuple> iterator() {
154    return new Iterator<Tuple>() {
155
156      final Iterator<?> memberIterator = members().iterator();
157      final Iterator<?> valuesIterator = values().iterator();
158
159      @Override
160      public boolean hasNext() {
161        return memberIterator.hasNext();
162      }
163
164      @Override
165      public Tuple next() {
166        return new Tuple(memberIterator.next(), valuesIterator.next());
167      }
168
169      @Override
170      public void remove() {
171        throw new UnsupportedOperationException();
172      }
173    };
174  }
175}