/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dx.rop.annotation; import com.android.dx.rop.cst.CstString; import com.android.dx.rop.cst.CstType; import com.android.dx.util.MutabilityControl; import com.android.dx.util.ToHuman; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.TreeMap; /** * An annotation on an element of a class. Annotations have an * associated type and additionally consist of a set of (name, value) * pairs, where the names are unique. */ public final class Annotation extends MutabilityControl implements Comparable, ToHuman { /** {@code non-null;} type of the annotation */ private final CstType type; /** {@code non-null;} the visibility of the annotation */ private final AnnotationVisibility visibility; /** {@code non-null;} map from names to {@link NameValuePair} instances */ private final TreeMap elements; /** * Construct an instance. It initially contains no elements. * * @param type {@code non-null;} type of the annotation * @param visibility {@code non-null;} the visibility of the annotation */ public Annotation(CstType type, AnnotationVisibility visibility) { if (type == null) { throw new NullPointerException("type == null"); } if (visibility == null) { throw new NullPointerException("visibility == null"); } this.type = type; this.visibility = visibility; this.elements = new TreeMap(); } /** {@inheritDoc} */ @Override public boolean equals(Object other) { if (! (other instanceof Annotation)) { return false; } Annotation otherAnnotation = (Annotation) other; if (! (type.equals(otherAnnotation.type) && (visibility == otherAnnotation.visibility))) { return false; } return elements.equals(otherAnnotation.elements); } /** {@inheritDoc} */ public int hashCode() { int hash = type.hashCode(); hash = (hash * 31) + elements.hashCode(); hash = (hash * 31) + visibility.hashCode(); return hash; } /** {@inheritDoc} */ public int compareTo(Annotation other) { int result = type.compareTo(other.type); if (result != 0) { return result; } result = visibility.compareTo(other.visibility); if (result != 0) { return result; } Iterator thisIter = elements.values().iterator(); Iterator otherIter = other.elements.values().iterator(); while (thisIter.hasNext() && otherIter.hasNext()) { NameValuePair thisOne = thisIter.next(); NameValuePair otherOne = otherIter.next(); result = thisOne.compareTo(otherOne); if (result != 0) { return result; } } if (thisIter.hasNext()) { return 1; } else if (otherIter.hasNext()) { return -1; } return 0; } /** {@inheritDoc} */ @Override public String toString() { return toHuman(); } /** {@inheritDoc} */ public String toHuman() { StringBuilder sb = new StringBuilder(); sb.append(visibility.toHuman()); sb.append("-annotation "); sb.append(type.toHuman()); sb.append(" {"); boolean first = true; for (NameValuePair pair : elements.values()) { if (first) { first = false; } else { sb.append(", "); } sb.append(pair.getName().toHuman()); sb.append(": "); sb.append(pair.getValue().toHuman()); } sb.append("}"); return sb.toString(); } /** * Gets the type of this instance. * * @return {@code non-null;} the type */ public CstType getType() { return type; } /** * Gets the visibility of this instance. * * @return {@code non-null;} the visibility */ public AnnotationVisibility getVisibility() { return visibility; } /** * Put an element into the set of (name, value) pairs for this instance. * If there is a preexisting element with the same name, it will be * replaced by this method. * * @param pair {@code non-null;} the (name, value) pair to place into this instance */ public void put(NameValuePair pair) { throwIfImmutable(); if (pair == null) { throw new NullPointerException("pair == null"); } elements.put(pair.getName(), pair); } /** * Add an element to the set of (name, value) pairs for this instance. * It is an error to call this method if there is a preexisting element * with the same name. * * @param pair {@code non-null;} the (name, value) pair to add to this instance */ public void add(NameValuePair pair) { throwIfImmutable(); if (pair == null) { throw new NullPointerException("pair == null"); } CstString name = pair.getName(); if (elements.get(name) != null) { throw new IllegalArgumentException("name already added: " + name); } elements.put(name, pair); } /** * Gets the set of name-value pairs contained in this instance. The * result is always unmodifiable. * * @return {@code non-null;} the set of name-value pairs */ public Collection getNameValuePairs() { return Collections.unmodifiableCollection(elements.values()); } }