001/**
002 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
003 *   This file is part of the LDP4j Project:
004 *     http://www.ldp4j.org/
005 *
006 *   Center for Open Middleware
007 *     http://www.centeropenmiddleware.com/
008 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
009 *   Copyright (C) 2014 Center for Open Middleware.
010 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
011 *   Licensed under the Apache License, Version 2.0 (the "License");
012 *   you may not use this file except in compliance with the License.
013 *   You may obtain a copy of the License at
014 *
015 *             http://www.apache.org/licenses/LICENSE-2.0
016 *
017 *   Unless required by applicable law or agreed to in writing, software
018 *   distributed under the License is distributed on an "AS IS" BASIS,
019 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020 *   See the License for the specific language governing permissions and
021 *   limitations under the License.
022 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
023 *   Artifact    : org.ldp4j.framework:ldp4j-application-api:0.1.0
024 *   Bundle      : ldp4j-application-api-0.1.0.jar
025 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
026 */
027package org.ldp4j.application.vocabulary;
028
029
030/**
031 * A term implementation that enforces the creation of immutable terms defined
032 * for an {@code AbstractImmutableVocabulary}, which behave as {@code Enum} constants.
033 *
034 * @version 1.0
035 * @since 1.0.0
036 * @author Miguel Esteban Gutiérrez
037 * @see AbstractImmutableVocabulary
038 */
039public class ImmutableTerm implements Term {
040
041  private static final long serialVersionUID = 6842492155071877731L;
042
043  private static final int HASH_CODE_SALT = 19;
044  private final int ordinal;
045  private final String name;
046  private final AbstractImmutableVocabulary<? extends ImmutableTerm> vocabulary;
047  private final String entityName;
048
049  public ImmutableTerm(AbstractImmutableVocabulary<? extends ImmutableTerm> vocabulary, String entityName) {
050    this.vocabulary = vocabulary;
051    this.entityName = entityName;
052    this.name = TermUtils.toTermName(entityName);
053    this.ordinal = vocabulary.reserveTermName(this.name);
054    vocabulary.registerTerm(this);
055  }
056
057  /**
058   * {@inheritDoc}
059   */
060  @Override
061  public final int ordinal() {
062    return ordinal;
063  }
064
065  /**
066   * {@inheritDoc}
067   */
068  @Override
069  public final String name() {
070    return name;
071  }
072
073  /**
074   * {@inheritDoc}
075   */
076  @Override
077  public final String entityName() {
078    return entityName;
079  }
080
081  /**
082   * {@inheritDoc}
083   */
084  @Override
085  public final String qualifiedEntityName() {
086    return vocabulary.getNamespace() + entityName;
087  }
088
089  /**
090   * {@inheritDoc}
091   */
092  @Override
093  public final Vocabulary<? extends ImmutableTerm> getDeclaringVocabulary() {
094    return vocabulary;
095  }
096
097  /**
098   * {@inheritDoc} <br/>
099   * This method may be overridden, though it typically isn't necessary or
100   * desirable. An term type should override this method when a more specific
101   * value types are supported.
102   */
103  @Override
104  public <V> V as(Class<? extends V> valueClass) {
105    try {
106      return TypeAdapter.adapt(this, valueClass);
107    } catch (CannotAdaptClassesException e) {
108      throw new UnsupportedOperationException("Class '"+getClass().getCanonicalName()+" cannot be transformed to '"+valueClass.getCanonicalName()+"'",e);
109    }
110  }
111
112  /**
113   * Returns true if the specified object is equal to this term constant.
114   *
115   * @param other
116   *            the object to be compared for equality with this object.
117   * @return true if the specified object is equal to this term constant.
118   */
119  @Override
120  public final boolean equals(Object other) {
121    return this == other;
122  }
123
124  /**
125   * Returns a hash code for this term constant.
126   *
127   * @return a hash code for this term constant.
128   */
129  @Override
130  public final int hashCode() {
131    return super.hashCode()*HASH_CODE_SALT;
132  }
133
134  /**
135   * Throws CloneNotSupportedException. This guarantees that terms are never
136   * cloned, which is necessary to preserve their "singleton" status.
137   *
138   * @return (never returns)
139   */
140  @Override
141  protected final Object clone() throws CloneNotSupportedException {
142    super.clone();
143    throw new CloneNotSupportedException();
144  }
145
146  /**
147   * Returns the qualified entity name of this term constant.
148   * This method may be overridden, though it typically isn't necessary or
149   * desirable. An term type should override this method when a more
150   * "programmer-friendly" string form exists.
151   *
152   * @return the name of this term constant
153   */
154  @Override
155  public String toString() {
156    return qualifiedEntityName();
157  }
158
159  /**
160   * Compares this term with the specified object for order. Returns a
161   * negative integer, zero, or a positive integer as this object is less
162   * than, equal to, or greater than the specified object.
163   *
164   * Term constants are only comparable to other term constants of the same
165   * vocabulary. The natural order implemented by this method is the order in
166   * which the constants are declared.
167   */
168  @Override
169  public int compareTo(Term other) {
170    ImmutableTerm self = this;
171    // TODO: This is too strict, replace by an ordering based on namespace, vocabulary identity, and then on the ordering within the namespace
172    if (self.getClass() != other.getClass() && // optimization
173      self.getDeclaringVocabulary() != other.getDeclaringVocabulary()) {
174      throw new ClassCastException();
175    }
176    return self.ordinal - other.ordinal();
177  }
178
179}