/* * 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.google.devtools.j2objc.ast; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import com.google.devtools.j2objc.util.ErrorUtil; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Node type for a com.google.j2objc.annotations.Property annotation. */ public class PropertyAnnotation extends Annotation { private final Set<String> attributes; private static final ImmutableList<String> PROPERTY_ATTRIBUTES = ImmutableList.of( "weak", "readonly", "copy", "assign", "nonatomic", "getter", "setter", "retain", "unsafe_unretained", "class", "nonnull", "nullable", "null_resettable", "null_unspecified", // Default values aren't kept, but may be listed in set when debugging. "atomic", "readwrite", "strong"); private static final Ordering<String> ATTRIBUTE_ORDERING = Ordering.explicit(PROPERTY_ATTRIBUTES); private static final Comparator<String> ATTRIBUTES_COMPARATOR = new Comparator<String>() { @Override public int compare(String a, String b) { if (a.startsWith("getter")) { a = "getter"; } if (a.startsWith("setter")) { a = "setter"; } if (b.startsWith("getter")) { b = "getter"; } if (b.startsWith("setter")) { b = "setter"; } return ATTRIBUTE_ORDERING.compare(a, b); } }; public PropertyAnnotation() { this.attributes = Sets.newHashSet(); } public PropertyAnnotation(PropertyAnnotation other) { super(other); this.attributes = new HashSet<String>(other.attributes); } @Override public Kind getKind() { return Kind.PROPERTY_ANNOTATION; } public void addAttribute(String attribute) { this.attributes.add(attribute); } public String getAttribute(String attribute) { for (String attr : attributes) { if (attr.startsWith(attribute)) { // Might be key=value string. return attr; } } return null; } public boolean hasAttribute(String attribute) { return getAttribute(attribute) != null; } public void removeAttribute(String attribute) { for (Iterator<String> iter = attributes.iterator(); iter.hasNext(); ) { String attr = iter.next(); if (attr.startsWith(attribute)) { // Might be key=value string. iter.remove(); break; } } } public String getGetter() { return getAttributeKeyValue("getter"); } public String getSetter() { return getAttributeKeyValue("setter"); } /** * Return the value for a specified key. Attributes are sequentially searched * rather than use a map, because most attributes are not key-value pairs. */ private String getAttributeKeyValue(String key) { String prefix = key + '='; String attribute = getAttribute(prefix); return attribute != null ? attribute.substring(prefix.length()) : null; } public Set<String> getPropertyAttributes() { return Sets.newHashSet(attributes); } /** * Return a sorted comma-separated list of the property attributes for this annotation. */ public static String toAttributeString(Set<String> attributes) { List<String> list = new ArrayList<String>(attributes); Collections.sort(list, ATTRIBUTES_COMPARATOR); return Joiner.on(", ").join(list); } @Override public PropertyAnnotation copy() { return new PropertyAnnotation(this); } @Override protected void acceptInner(TreeVisitor visitor) { visitor.visit(this); visitor.endVisit(this); } @Override public void validateInner() { // Validate after node is fully integrated into AST, so error() can find line info. if (TreeUtil.getCompilationUnit(this) != null) { for (String attr : this.attributes) { if (!attr.startsWith("getter=") && !attr.startsWith("setter=") // Accessors checked later. && !PROPERTY_ATTRIBUTES.contains(attr)) { ErrorUtil.error(this, "Invalid @Property attribute: " + attr); } } } } }