/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * IdentifierInfo.java * Created: Sep 5, 2007 * By: Joseph Wong */ package org.openquark.cal.compiler; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import org.openquark.util.Pair; /** * This is the root class of a hierarchy of classes representing different kinds of identifiers in * CAL. Each subclass of this class represents the <i>identity</i> of a particular kind of CAL * entity. For example, a top-level function is represented by an * {@link org.openquark.cal.compiler.IdentifierInfo.TopLevel.FunctionOrClassMethod IdentifierInfo.TopLevel.FunctionOrClassMethod}. * * <p> * This is independent of the <i>context</i> in which the actual identifier appears in source: for * example, the name of a top-level function in its definition must be unqualified, while a * reference to the same function in an expression is <i>qualifiable</i> (can be qualified) - both * occurrences would be associated with the same {@link IdentifierInfo}. These two occurrences can * be distinguished by their representing {@link IdentifierOccurrence} - the former would be an * {@link org.openquark.cal.compiler.IdentifierOccurrence.Binding.Definition IdentifierOccurrence.Binding.Definition}, * while the latter would be an * {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.Qualifiable IdentifierOccurrence.Reference.Qualifiable}. * * <p> * At the same time, some concepts which are grammatically similar are distinguished by different subclasses, e.g. * a record field name vs a data constructor field name - while both can be either textual or ordinal, the identity of the * latter also includes the data constructor(s) associated with the field name. * * <p> * The hierarchy of classes and what they represent are as follows: * * <ul> * <li>a module name * - {@link org.openquark.cal.compiler.IdentifierInfo.Module Module} * <li>a name to a top-level (or scoped) entity * - {@link org.openquark.cal.compiler.IdentifierInfo.TopLevel TopLevel} * <ul> * <li>function/class method * - {@link org.openquark.cal.compiler.IdentifierInfo.TopLevel.FunctionOrClassMethod FunctionOrClassMethod} * <li>type constructor * - {@link org.openquark.cal.compiler.IdentifierInfo.TopLevel.TypeCons TypeCons} * <li>data constructor * - {@link org.openquark.cal.compiler.IdentifierInfo.TopLevel.DataCons DataCons} * <li>type class * - {@link org.openquark.cal.compiler.IdentifierInfo.TopLevel.TypeClass TypeClass} * </ul> * <li>a local variable * - {@link org.openquark.cal.compiler.IdentifierInfo.Local Local} * <ul> * <li>local function * - {@link org.openquark.cal.compiler.IdentifierInfo.Local.Function Function} * <li>pattern variable in a (lazy) local pattern match declaration * - {@link org.openquark.cal.compiler.IdentifierInfo.Local.PatternMatchVariable PatternMatchVariable} * <li>pattern variable in a case expression alternative * - {@link org.openquark.cal.compiler.IdentifierInfo.Local.CasePatternVariable CasePatternVariable} * <li>parameter * - {@link org.openquark.cal.compiler.IdentifierInfo.Local.Parameter Parameter} * <ul> * <li>...of a top-level function/class method * - {@link org.openquark.cal.compiler.IdentifierInfo.Local.Parameter.TopLevelFunctionOrClassMethod TopLevelFunctionOrClassMethod} * <li>...of a local function * - {@link org.openquark.cal.compiler.IdentifierInfo.Local.Parameter.LocalFunction LocalFunction} * <li>...of a lambda expression * - {@link org.openquark.cal.compiler.IdentifierInfo.Local.Parameter.Lambda Lambda} * <li>...of an instance method (declared in CALDoc) * - {@link org.openquark.cal.compiler.IdentifierInfo.Local.Parameter.InstanceMethodCALDoc InstanceMethodCALDoc} * </ul> * </ul> * <li>a data constructor field name * - {@link org.openquark.cal.compiler.IdentifierInfo.DataConsFieldName DataConsFieldName} * <li>a record field name * - {@link org.openquark.cal.compiler.IdentifierInfo.RecordFieldName RecordFieldName} * <li>a type variable * - {@link org.openquark.cal.compiler.IdentifierInfo.TypeVariable TypeVariable} * </ul> * * @see IdentifierOccurrence * IdentifierOccurrence - * this is the closely related class representing an identifier in the context in which it appears * (e.g. a binding vs a reference, qualifiable vs non-qualifiable) * * @author Joseph Wong * @author Iulian Radu */ /* * @history * * The representation of the different kinds of identifiers via a class hierarchy (as opposed to an enum-based representation) * is inspired by the SourceModel, by Bo Ilic. * * Many of the different kinds of identifiers have also been represented by the typesafe-enum SourceIdentifier.Category, * by Iulian Radu. */ /* * Note: Eclipse compilation may mistakenly report compile errors related to cycles in the type hierarchy, etc of the classes in this file. * You can make a small edit to the IdentifierInfo.TopLevel nested class to make Eclipse recompile without errors. */ public abstract class IdentifierInfo { /** * Represents the identity of a module referred to by a module name identifier, namely the module's fully qualified name. * * @author Joseph Wong */ public static final class Module extends IdentifierInfo { /** * The resolved, fully qualified name of the module referred to by the identifier. */ private final ModuleName resolvedName; /** * Constructs an instance of this identifier info. * @param resolvedName the resolved, fully qualified name of the module referred to by the identifier. */ public Module(final ModuleName resolvedName) { if (resolvedName == null) { throw new NullPointerException(); } this.resolvedName = resolvedName; } /** * @return the resolved, fully qualified name of the module referred to by the identifier. */ public ModuleName getResolvedName() { return resolvedName; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { return obj instanceof Module && ((Module)obj).getResolvedName().equals(getResolvedName()); } /** * {@inheritDoc} */ @Override public int hashCode() { return getResolvedName().hashCode(); } /** * {@inheritDoc} */ @Override public String toString() { return toStringOneField("Module", "resolvedName", getResolvedName()); } } /** * Abstract base class for the identity of a top-level (or scoped) entity referred to by an identifier. * * @author Joseph Wong */ public static abstract class TopLevel extends IdentifierInfo { /* * Note: Eclipse compilation may mistakenly report compile errors related to cycles in the type hierarchy, etc of the classes in this file. * You can make a small edit to this nested class to make Eclipse recompile without errors. */ /** * The resolved, fully qualified name of the entity referred to by the identifier. */ private final QualifiedName resolvedName; /** * Represents the identity of a top-level function or class method referred to by an identifier, namely the * function or method's fully qualified name. * * @author Joseph Wong */ public static final class FunctionOrClassMethod extends TopLevel { /** * Constructs an instance of this identifier info. * @param resolvedName the resolved, fully qualified name of the entity referred to by the identifier. */ public FunctionOrClassMethod(final QualifiedName resolvedName) { super(resolvedName); } /** * {@inheritDoc} */ @Override public String toString() { return toStringOneField("TopLevel.FunctionOrClassMethod", "resolvedName", getResolvedName()); } } /** * Represents the identity of a type constructor referred to by an identifier, namely the * type constructor's fully qualified name. * * @author Joseph Wong */ public static final class TypeCons extends TopLevel { /** * Constructs an instance of this identifier info. * @param resolvedName the resolved, fully qualified name of the entity referred to by the identifier. */ public TypeCons(final QualifiedName resolvedName) { super(resolvedName); } /** * {@inheritDoc} */ @Override public String toString() { return toStringOneField("TopLevel.TypeCons", "resolvedName", getResolvedName()); } } /** * Represents the identity of a data constructor referred to by an identifier, namely the * data constructor's fully qualified name. * * @author Joseph Wong */ public static final class DataCons extends TopLevel { /** * Constructs an instance of this identifier info. * @param resolvedName the resolved, fully qualified name of the entity referred to by the identifier. */ public DataCons(final QualifiedName resolvedName) { super(resolvedName); } /** * {@inheritDoc} */ @Override public String toString() { return toStringOneField("TopLevel.DataCons", "resolvedName", getResolvedName()); } } /** * Represents the identity of a type class referred to by an identifier, namely the * type class's fully qualified name. * * @author Joseph Wong */ public static final class TypeClass extends TopLevel { /** * Constructs an instance of this identifier info. * @param resolvedName the resolved, fully qualified name of the entity referred to by the identifier. */ public TypeClass(final QualifiedName resolvedName) { super(resolvedName); } /** * {@inheritDoc} */ @Override public String toString() { return toStringOneField("TopLevel.TypeClass", "resolvedName", getResolvedName()); } } /** * Private constructor. Only meant to be called by inner subclasses. * @param resolvedName the resolved, fully qualified name of the entity referred to by the identifier. */ private TopLevel(final QualifiedName resolvedName) { if (resolvedName == null) { throw new NullPointerException(); } this.resolvedName = resolvedName; } /** * @return the resolved, fully qualified name of the entity referred to by the identifier. */ public QualifiedName getResolvedName() { return resolvedName; } /** * {@inheritDoc} */ @Override public final boolean equals(final Object obj) { return obj != null && obj.getClass().equals(this.getClass()) && ((TopLevel)obj).getResolvedName().equals(getResolvedName()); } /** * {@inheritDoc} */ @Override public final int hashCode() { return getResolvedName().hashCode(); } } /** * Abstract base class for the identity of a locally-defined entity referred to by an identifier. * * @author Joseph Wong */ public static abstract class Local extends IdentifierInfo { /** * The name of the local variable, as appearing in source. */ private final String varName; /** * An enum representing the different kinds of pattern variables. A pattern variable's kind * is determined by the context in which it is defined. * * @author Joseph Wong */ public static enum PatternVariableKind { /** * A regular, non-punned pattern variable (e.g. bar in {foo=bar}) */ regular, /** * A punned data constructor field pattern (e.g. bar in DC {bar}) */ punnedDataConsField, /** * A punned record field pattern (e.g. bar in {r|bar}) */ punnedRecordField } /** * Represents the identity of a case-bound pattern variable referred to by an identifier. * * @author Joseph Wong */ public static final class CasePatternVariable extends Local { /** * The unique ID for the pattern variable. */ // todo-jowong this is currently not assigned by the canonical LocalFunctionIdentifierGenerator that // generates unique IDs for let-bound functions and local pattern match variables. private final LocalFunctionIdentifier casePatternVariableIdentifier; /** * The kind of pattern variable represented. */ private final PatternVariableKind kind; /** * Constructs an instance of this identifier info. * @param varName the name of the pattern variable, as appearing in source. * @param casePatternVariableIdentifier the unique ID for the pattern variable. * @param kind the kind of pattern variable represented. */ public CasePatternVariable(final String varName, final LocalFunctionIdentifier casePatternVariableIdentifier, final PatternVariableKind kind) { super(varName); if (casePatternVariableIdentifier == null || kind == null) { throw new NullPointerException(); } this.casePatternVariableIdentifier = casePatternVariableIdentifier; this.kind = kind; } /** * @return the unique ID for the pattern variable. */ public LocalFunctionIdentifier getCasePatternVariableIdentifier() { return casePatternVariableIdentifier; } /** * @return the kind of pattern variable represented. */ public PatternVariableKind getKind() { return kind; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj instanceof CasePatternVariable) { final CasePatternVariable other = (CasePatternVariable)obj; return other.getVarName().equals(getVarName()) && other.getCasePatternVariableIdentifier().equals(getCasePatternVariableIdentifier()) && other.getKind().equals(getKind()); } else { return false; } } /** * {@inheritDoc} */ @Override public int hashCode() { return hashThreeFields(getVarName(), getCasePatternVariableIdentifier(), getKind()); } /** * {@inheritDoc} */ @Override public String toString() { return toStringThreeFields("Local.CasePatternVariable", "varName", getVarName(), "kind", getKind(), "casePatternVariableIdentifier", getCasePatternVariableIdentifier()); } } /** * Represents the identity of a let-bound local function referred to by an identifier. * * @author Joseph Wong */ public static final class Function extends Local { /** * The canonical unique ID for the local function. */ private final LocalFunctionIdentifier localFunctionIdentifier; /** * Constructs an instance of this identifier info. * @param varName the name of the local function, as appearing in source. * @param localFunctionIdentifier the canonical unique ID for the local function. */ public Function(final String varName, final LocalFunctionIdentifier localFunctionIdentifier) { super(varName); if (localFunctionIdentifier == null) { throw new NullPointerException(); } this.localFunctionIdentifier = localFunctionIdentifier; } /** * @return the canonical unique ID for the local function. */ public LocalFunctionIdentifier getLocalFunctionIdentifier() { return localFunctionIdentifier; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj instanceof Function) { final Function other = (Function)obj; return other.getVarName().equals(getVarName()) && other.getLocalFunctionIdentifier().equals(getLocalFunctionIdentifier()); } else { return false; } } /** * {@inheritDoc} */ @Override public int hashCode() { return hashTwoFields(getVarName(), getLocalFunctionIdentifier()); } /** * {@inheritDoc} */ @Override public String toString() { return toStringTwoFields("Local.Function", "varName", getVarName(), "localFunctionIdentifier", getLocalFunctionIdentifier()); } } /** * Represents the identity of a let-bound pattern variable appearing in a (lazy) local pattern match declaration. * * @author Joseph Wong */ public static final class PatternMatchVariable extends Local { /** * The kind of pattern variable represented. */ private final PatternVariableKind kind; /** * The canonical unique ID for the local function. */ private final LocalFunctionIdentifier localFunctionIdentifier; /** * Constructs an instance of this identifier info. * @param varName the name of the pattern variable, as appearing in source. * @param kind the kind of pattern variable represented. * @param localFunctionIdentifier the canonical unique ID for the local function. */ public PatternMatchVariable(final String varName, final PatternVariableKind kind, final LocalFunctionIdentifier localFunctionIdentifier) { super(varName); if (kind == null || localFunctionIdentifier == null) { throw new NullPointerException(); } this.kind = kind; this.localFunctionIdentifier = localFunctionIdentifier; } /** * @return the kind of pattern variable represented. */ public PatternVariableKind getKind() { return kind; } /** * @return the canonical unique ID for the local function. */ public LocalFunctionIdentifier getLocalFunctionIdentifier() { return localFunctionIdentifier; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj instanceof PatternMatchVariable) { final PatternMatchVariable other = (PatternMatchVariable)obj; return other.getVarName().equals(getVarName()) && other.getKind().equals(getKind()) && other.getLocalFunctionIdentifier().equals(getLocalFunctionIdentifier()); } else { return false; } } /** * {@inheritDoc} */ @Override public int hashCode() { return hashThreeFields(getVarName(), getKind(), getLocalFunctionIdentifier()); } /** * {@inheritDoc} */ @Override public String toString() { return toStringThreeFields("Local.PatternMatchVariable", "varName", getVarName(), "kind", getKind(), "localFunctionIdentifier", getLocalFunctionIdentifier()); } } /** * Abstract base class for the identity of a parameter, which can be defined as part of a function's parameter list, * or in a CALDoc arg tag. * * @author Joseph Wong */ public static abstract class Parameter extends Local { /** * Represents the identity of a lambda-bound parameter. * * @author Joseph Wong */ public static final class Lambda extends Parameter { /** * The unique ID for the parameter. */ // todo-jowong this is currently not assigned by the canonical LocalFunctionIdentifierGenerator that // generates unique IDs for let-bound functions and local pattern match variables. private final LocalFunctionIdentifier lambdaParameterIdentifier; /** * Constructs an instance of this identifier info. * @param varName the name of the lambda parameter, as appearing in source. * @param lambdaParameterIdentifier the unique ID for the parameter. */ public Lambda(final String varName, final LocalFunctionIdentifier lambdaParameterIdentifier) { super(varName); if (lambdaParameterIdentifier == null) { throw new NullPointerException(); } this.lambdaParameterIdentifier = lambdaParameterIdentifier; } /** * @return the unique ID for parameter. */ public LocalFunctionIdentifier getLambdaParameterIdentifier() { return lambdaParameterIdentifier; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj instanceof Lambda) { final Lambda other = (Lambda)obj; return other.getVarName().equals(getVarName()) && other.getLambdaParameterIdentifier().equals(getLambdaParameterIdentifier()); } else { return false; } } /** * {@inheritDoc} */ @Override public int hashCode() { return hashTwoFields(getVarName(), getLambdaParameterIdentifier()); } /** * {@inheritDoc} */ @Override public String toString() { return toStringTwoFields("Local.Parameter.Lambda", "varName", getVarName(), "lambdaParameterIdentifier", getLambdaParameterIdentifier()); } } /** * Represents the identity of a parameter of an instance method as declared in the associated CALDoc comment. * * @author Joseph Wong */ // todo-jowong the identity should really contain the qualified class method name, the qualified type name, and the defining module name public static final class InstanceMethodCALDoc extends Parameter { /** * Constructs an instance of this identifier info. * @param varName the name of the parameter, as appearing in source. */ public InstanceMethodCALDoc(final String varName) { super(varName); } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { return obj instanceof InstanceMethodCALDoc && ((InstanceMethodCALDoc)obj).getVarName().equals(getVarName()); } /** * {@inheritDoc} */ @Override public int hashCode() { return getVarName().hashCode(); } /** * {@inheritDoc} */ @Override public String toString() { return toStringOneField("Local.Parameter.InstanceMethodCALDoc", "varName", getVarName()); } } /** * Represents the identity of a parameter of a let-bound local function, which can be * defined as part of the function's parameter list, or in the associated CALDoc comment. * * @author Joseph Wong */ public static final class LocalFunction extends Parameter { /** * The identity of the associated local function. */ public final Local.Function associatedFunction; /** * Constructs an instance of this identifier info. * @param varName the name of the local function parameter, as appearing in source. * @param associatedFunction the identity of the associated local function. */ public LocalFunction(final String varName, final Local.Function associatedFunction) { super(varName); if (associatedFunction == null) { throw new NullPointerException(); } this.associatedFunction = associatedFunction; } /** * @return the identity of the associated local function. */ public Local.Function getAssociatedFunction() { return associatedFunction; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj instanceof LocalFunction) { final LocalFunction other = (LocalFunction)obj; return other.getAssociatedFunction().equals(getAssociatedFunction()) && other.getVarName().equals(getVarName()); } else { return false; } } /** * {@inheritDoc} */ @Override public int hashCode() { return hashTwoFields(getAssociatedFunction(), getVarName()); } /** * {@inheritDoc} */ @Override public String toString() { return toStringTwoFields("Local.Parameter.LocalFunction", "varName", getVarName(), "associatedFunction", getAssociatedFunction()); } } /** * Represents the identity of a parameter of a top-level function or class method, which can be * defined as part of the function's parameter list, or in the associated CALDoc comment. * * @author Joseph Wong */ public static final class TopLevelFunctionOrClassMethod extends Parameter { /** * The identity of the associated function/method. */ public final TopLevel.FunctionOrClassMethod associatedFunction; /** * Constructs an instance of this identifier info. * @param varName the name of the function/method parameter, as appearing in source. * @param associatedFunction the identity of the associated function/method. */ public TopLevelFunctionOrClassMethod(final String varName, final TopLevel.FunctionOrClassMethod associatedFunction) { super(varName); if (associatedFunction == null) { throw new NullPointerException(); } this.associatedFunction = associatedFunction; } /** * @return the identity of the associated function/method. */ public TopLevel.FunctionOrClassMethod getAssociatedFunction() { return associatedFunction; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj instanceof TopLevelFunctionOrClassMethod) { final TopLevelFunctionOrClassMethod other = (TopLevelFunctionOrClassMethod)obj; return other.getAssociatedFunction().equals(getAssociatedFunction()) && other.getVarName().equals(getVarName()); } else { return false; } } /** * {@inheritDoc} */ @Override public int hashCode() { return hashTwoFields(getAssociatedFunction(), getVarName()); } /** * {@inheritDoc} */ @Override public String toString() { return toStringTwoFields("Local.Parameter.TopLevelFunctionOrClassMethod", "varName", getVarName(), "associatedFunction", getAssociatedFunction()); } } /** * Private constructor. Only meant to be called by inner subclasses. * @param varName the name of the parameter, as appearing in source. */ private Parameter(final String varName) { super(varName); } } /** * Private constructor. Only meant to be called by inner subclasses. * @param varName the name of the local variable, as appearing in source. */ private Local(final String varName) { if (varName == null) { throw new NullPointerException(); } this.varName = varName; } /** * @return the name of the local variable, as appearing in source. */ public String getVarName() { return varName; } } /** * Represents the identity of one or more data constructor fields referred to by an identifier, * which consists of the (common) field name and the identities of the associated data constructors. * * <p> * A data constructor field name can be associated with multiple data constructors when it appears * in a case alternative containing more than one data constructor, e.g. * <pre> * case foo of * (DC1|DC2|DC3) {field1=bar, field2} -> ... * </pre> * Both field1 and field2 are associated with DC1, DC2, and DC3. * * <p> * This class encapsulates <i>one or more</i> associated data constructors. This is part of the design * of the IdentifierOccurrence class, which is in a strict 1-to-1 relationship with an IdentifierInfo. * * <p> * Thus, to check whether two data constructor field names may refer to the same field (or fields), one should * check whether the set of data constructors overlap (and whether the field names match), rather than rely * on the strict equality semantics implemented in equals(). * * @author Joseph Wong */ public static final class DataConsFieldName extends IdentifierInfo { /** * The name of the field. */ private final FieldName fieldName; /** * The set of identities of the associated data constructors. This is a set because order does not matter * for the identity of this data constructor field name. */ private final Set<TopLevel.DataCons> associatedDataConstructors; /** * Constructs an instance of this identifier info. * @param fieldName the name of the field. * @param associatedDataConstructors the set of identities of the associated data constructors. */ public DataConsFieldName(final FieldName fieldName, final Collection<TopLevel.DataCons> associatedDataConstructors) { if (fieldName == null || associatedDataConstructors == null) { throw new NullPointerException(); } this.fieldName = fieldName; this.associatedDataConstructors = Collections.unmodifiableSet(new LinkedHashSet<TopLevel.DataCons>(associatedDataConstructors)); if (this.associatedDataConstructors.isEmpty()) { throw new IllegalArgumentException("there must be at least one associated data constructor"); } } /** * @return the name of the field. */ public FieldName getFieldName() { return fieldName; } /** * @return the set of identities of the associated data constructors. */ public Set<TopLevel.DataCons> getAssociatedDataConstructors() { return associatedDataConstructors; } /** * @return the identity of the first associated data constructor. The ordering of data constructors follows from the * ordering of the collection specified on construction of this instance. */ public TopLevel.DataCons getFirstAssociatedDataConstructor() { return associatedDataConstructors.iterator().next(); } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj instanceof DataConsFieldName) { final DataConsFieldName other = (DataConsFieldName)obj; return other.getFieldName().getCalSourceForm().equals(getFieldName().getCalSourceForm()) && other.getAssociatedDataConstructors().equals(getAssociatedDataConstructors()); } else { return false; } } /** * {@inheritDoc} */ @Override public int hashCode() { return hashTwoFields(getFieldName().getCalSourceForm(), getAssociatedDataConstructors()); } /** * {@inheritDoc} */ @Override public String toString() { return toStringTwoFields("DataConsFieldName", "fieldName", getFieldName().getCalSourceForm(), "associatedDataConstructors", getAssociatedDataConstructors()); } } /** * Represents the identity of a record field referred to by an identifier. This is distinguished from * a data constructor field name, which is associated with one or more data constructors. * * @author Joseph Wong */ public static final class RecordFieldName extends IdentifierInfo { /** * The name of the field. */ private final FieldName fieldName; /** * Constructs an instance of this identifier info. * @param fieldName the name of the field. */ public RecordFieldName(final FieldName fieldName) { if (fieldName == null) { throw new NullPointerException(); } this.fieldName = fieldName; } /** * @return the name of the field. */ public FieldName getFieldName() { return fieldName; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { return obj instanceof RecordFieldName && ((RecordFieldName)obj).getFieldName().getCalSourceForm().equals(getFieldName().getCalSourceForm()); } /** * {@inheritDoc} */ @Override public int hashCode() { return getFieldName().getCalSourceForm().hashCode(); } /** * {@inheritDoc} */ @Override public String toString() { return toStringOneField("RecordFieldName", "fieldName", getFieldName().getCalSourceForm()); } } /** * Represents the identity of a type variable referred to by an identifier, namely the variable name and * the variable's unique ID. * * @author Joseph Wong */ /* * @discussion * * A source-position-based unique ID scheme is currently implemented for type variables. This means that * a type variable's identity is globally unique, but is invalidated by source changes. However, this also means that * the unique ID is invariant under different source tree traversal schemes. * * An alternative is to assign a counter-based ID to the type variables, but that means the generated IDs are sensitive * to the order of traversal. Thus to get consistent IDs for all type variables, the all type variables in * a module definition needs to be traversed completely every time. */ public static final class TypeVariable extends IdentifierInfo { /** * The name of the type variable. */ private final String typeVarName; /** * The unique ID for the type variable. */ private final Pair<ModuleName, SourcePosition> typeVarUniqueIdentifier; /** * Constructs an instance of this identifier info. * @param typeVarName the name of the type variable. * @param typeVarUniqueIdentifier the unique ID for the type variable. */ public TypeVariable(final String typeVarName, final Pair<ModuleName, SourcePosition> typeVarUniqueIdentifier) { if (typeVarName == null) { throw new NullPointerException(); } this.typeVarName = typeVarName; this.typeVarUniqueIdentifier = typeVarUniqueIdentifier; } /** * @return the name of the type variable. */ public String getTypeVarName() { return typeVarName; } /** * @return the unique ID for the type variable. */ public Pair<ModuleName, SourcePosition> getTypeVarUniqueIdentifier() { return typeVarUniqueIdentifier; } /** * {@inheritDoc} */ @Override public boolean equals(final Object obj) { if (obj instanceof TypeVariable) { final TypeVariable other = (TypeVariable)obj; return other.getTypeVarName().equals(getTypeVarName()) && other.getTypeVarUniqueIdentifier().equals(getTypeVarUniqueIdentifier()); } else { return false; } } /** * {@inheritDoc} */ @Override public int hashCode() { return hashTwoFields(getTypeVarName(), getTypeVarUniqueIdentifier()); } /** * {@inheritDoc} */ @Override public String toString() { return toStringTwoFields("TypeVariable", "typeVarName", getTypeVarName(), "typeVarUniqueIdentifier", getTypeVarUniqueIdentifier()); } } /** Private constructor. Only meant to be called by inner subclasses. */ private IdentifierInfo() {} /** * Helper method to hash an object with two fields. * @param field1 the first field. * @param field2 the second field. * @return a hash value. */ private static int hashTwoFields(final Object field1, final Object field2) { return (37 * (37 * 17 + field1.hashCode())) + field2.hashCode(); } /** * Helper method to hash an object with three fields. * @param field1 the first field. * @param field2 the second field. * @param field3 the third field. * @return a hash value. */ private static int hashThreeFields(final Object field1, final Object field2, final Object field3) { int result = 17; result = (result * 37) + field1.hashCode(); result = (result * 37) + field2.hashCode(); result = (result * 37) + field3.hashCode(); return result; } /** * Helper method to produce a string representation of an object with one field. * @param className the short name of the class (after "IdentifierInfo.") * @param label1 the first field's name. * @param field1 the first field. * @return the string representation. */ private static String toStringOneField(final String className, final String label1, final Object field1) { return "[IdentifierInfo." + className + " " + label1 + "=" + field1 + "]"; } /** * Helper method to produce a string representation of an object with two field. * @param className the short name of the class (after "IdentifierInfo.") * @param label1 the first field's name. * @param field1 the first field. * @param label2 the second field's name. * @param field2 the second field. * @return the string representation. */ private static String toStringTwoFields(final String className, final String label1, final Object field1, final String label2, final Object field2) { return "[IdentifierInfo." + className + " " + label1 + "=" + field1 + " " + label2 + "=" + field2 + "]"; } /** * Helper method to produce a string representation of an object with three field. * @param className the short name of the class (after "IdentifierInfo.") * @param label1 the first field's name. * @param field1 the first field. * @param label2 the second field's name. * @param field2 the second field. * @param label3 the third field's name. * @param field3 the third field. * @return the string representation. */ private static String toStringThreeFields(final String className, final String label1, final Object field1, final String label2, final Object field2, final String label3, final Object field3) { return "[IdentifierInfo." + className + " " + label1 + "=" + field1 + " " + label2 + "=" + field2 + " " + label3 + "=" + field3 + "]"; } /// All subclasses must implement equals, hashCode, and toString /** * {@inheritDoc} */ @Override public abstract boolean equals(Object obj); /** * {@inheritDoc} */ @Override public abstract int hashCode(); /** * {@inheritDoc} */ @Override public abstract String toString(); }