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