jfranck@1313: /* jjg@1492: * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. jfranck@1313: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jfranck@1313: * jfranck@1313: * This code is free software; you can redistribute it and/or modify it jfranck@1313: * under the terms of the GNU General Public License version 2 only, as jfranck@1313: * published by the Free Software Foundation. Oracle designates this jfranck@1313: * particular file as subject to the "Classpath" exception as provided jfranck@1313: * by Oracle in the LICENSE file that accompanied this code. jfranck@1313: * jfranck@1313: * This code is distributed in the hope that it will be useful, but WITHOUT jfranck@1313: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jfranck@1313: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jfranck@1313: * version 2 for more details (a copy is included in the LICENSE file that jfranck@1313: * accompanied this code). jfranck@1313: * jfranck@1313: * You should have received a copy of the GNU General Public License version jfranck@1313: * 2 along with this work; if not, write to the Free Software Foundation, jfranck@1313: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jfranck@1313: * jfranck@1313: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jfranck@1313: * or visit www.oracle.com if you need additional information or have any jfranck@1313: * questions. jfranck@1313: */ jfranck@1313: jfranck@1313: package com.sun.tools.javac.code; jfranck@1313: jfranck@1313: import java.util.Map; jjg@1357: jfranck@1313: import javax.tools.JavaFileObject; jfranck@1313: jfranck@1313: import com.sun.tools.javac.comp.Annotate; jfranck@1313: import com.sun.tools.javac.comp.AttrContext; jfranck@1313: import com.sun.tools.javac.comp.Env; jjg@1357: import com.sun.tools.javac.util.*; jfranck@1313: import com.sun.tools.javac.util.Assert; jfranck@1313: import com.sun.tools.javac.util.List; jfranck@1313: import com.sun.tools.javac.util.Log; jfranck@1313: import com.sun.tools.javac.util.Pair; jfranck@1313: import static com.sun.tools.javac.code.Kinds.PCK; jfranck@1313: jfranck@1313: /** jfranck@1313: * Container for all annotations (attributes in javac) on a Symbol. jfranck@1313: * jfranck@1313: * This class is explicitly mutable. Its contents will change when attributes jfranck@1313: * are annotated onto the Symbol. However this class depends on the facts that jfranck@1313: * List (in javac) is immutable. jfranck@1313: * jfranck@1313: * An instance of this class can be in one of three states: jfranck@1313: * jjg@1521: * NOT_STARTED indicates that the Symbol this instance belongs to has not been jfranck@1313: * annotated (yet). Specifically if the declaration is not annotated this jfranck@1313: * instance will never move past NOT_STARTED. You can never go back to jfranck@1313: * NOT_STARTED. jfranck@1313: * jfranck@1313: * IN_PROGRESS annotations have been found on the declaration. Will be processed jfranck@1313: * later. You can reset to IN_PROGRESS. While IN_PROGRESS you can set the list jfranck@1313: * of attributes (and this moves out of the IN_PROGRESS state). jfranck@1313: * jfranck@1313: * "unnamed" this Annotations contains some attributes, possibly the final set. jfranck@1313: * While in this state you can only prepend or append to the attributes not set jjg@1521: * it directly. You can also move back to the IN_PROGRESS state using reset(). jfranck@1313: * jfranck@1313: *

This is NOT part of any supported API. If you write code that depends jfranck@1313: * on this, you do so at your own risk. This code and its internal interfaces jfranck@1313: * are subject to change or deletion without notice. jfranck@1313: */ jfranck@1313: public class Annotations { jfranck@1313: jjg@1521: private static final List DECL_NOT_STARTED = List.of(null); jjg@1521: private static final List DECL_IN_PROGRESS = List.of(null); jjg@1521: jfranck@1313: /* jfranck@1313: * This field should never be null jfranck@1313: */ jjg@1521: private List attributes = DECL_NOT_STARTED; jjg@1521: jfranck@1313: /* jjg@1521: * This field should never be null jjg@1521: */ jjg@1521: private List type_attributes = List.nil(); jjg@1521: jjg@1521: /* jjg@1521: * The Symbol this Annotations instance belongs to jfranck@1313: */ jfranck@1445: private final Symbol sym; jfranck@1313: jfranck@1445: public Annotations(Symbol sym) { jfranck@1445: this.sym = sym; jfranck@1313: } jfranck@1313: jjg@1521: public List getDeclarationAttributes() { jjg@1521: return filterDeclSentinels(attributes); jfranck@1313: } jfranck@1313: jjg@1521: public List getTypeAttributes() { jjg@1521: return type_attributes; jjg@1521: } jjg@1521: jjg@1521: public void setDeclarationAttributes(List a) { jfranck@1313: Assert.check(pendingCompletion() || !isStarted()); jfranck@1313: if (a == null) { jfranck@1313: throw new NullPointerException(); jfranck@1313: } jfranck@1313: attributes = a; jfranck@1313: } jfranck@1313: jjg@1521: public void setTypeAttributes(List a) { jjg@1521: if (a == null) { jjg@1521: throw new NullPointerException(); jjg@1521: } jjg@1521: type_attributes = a; jjg@1521: } jjg@1521: jfranck@1313: public void setAttributes(Annotations other) { jfranck@1313: if (other == null) { jfranck@1313: throw new NullPointerException(); jfranck@1313: } jjg@1521: setDeclarationAttributes(other.getDeclarationAttributes()); jjg@1521: setTypeAttributes(other.getTypeAttributes()); jfranck@1313: } jfranck@1313: jjg@1521: public void setDeclarationAttributesWithCompletion(final Annotate.AnnotateRepeatedContext ctx) { jfranck@1445: Assert.check(pendingCompletion() || (!isStarted() && sym.kind == PCK)); jjg@1521: this.setDeclarationAttributes(getAttributesForCompletion(ctx)); jjg@1521: } jfranck@1313: jjg@1521: public void appendTypeAttributesWithCompletion(final Annotate.AnnotateRepeatedContext ctx) { jjg@1521: this.appendUniqueTypes(getAttributesForCompletion(ctx)); jjg@1521: } jjg@1521: jjg@1521: private List getAttributesForCompletion( jjg@1521: final Annotate.AnnotateRepeatedContext ctx) { jjg@1521: jjg@1521: Map> annotated = ctx.annotated; jfranck@1313: boolean atLeastOneRepeated = false; jjg@1521: List buf = List.nil(); jjg@1521: for (ListBuffer lb : annotated.values()) { jfranck@1313: if (lb.size() == 1) { jfranck@1313: buf = buf.prepend(lb.first()); jfranck@1313: } else { // repeated jjg@1521: // This will break when other subtypes of Attributs.Compound jjg@1521: // are introduced, because PlaceHolder is a subtype of TypeCompound. jjg@1521: T res; jjg@1521: @SuppressWarnings("unchecked") jjg@1521: T ph = (T) new Placeholder(ctx, lb.toList(), sym); jjg@1521: res = ph; jjg@1521: buf = buf.prepend(res); jfranck@1313: atLeastOneRepeated = true; jfranck@1313: } jfranck@1313: } jfranck@1313: jfranck@1313: if (atLeastOneRepeated) { jfranck@1313: // The Symbol s is now annotated with a combination of jfranck@1313: // finished non-repeating annotations and placeholders for jfranck@1313: // repeating annotations. jfranck@1313: // jfranck@1313: // We need to do this in two passes because when creating jfranck@1313: // a container for a repeating annotation we must jjg@1492: // guarantee that the @Repeatable on the jfranck@1313: // contained annotation is fully annotated jfranck@1313: // jfranck@1313: // The way we force this order is to do all repeating jfranck@1313: // annotations in a pass after all non-repeating are jjg@1492: // finished. This will work because @Repeatable jfranck@1313: // is non-repeating and therefore will be annotated in the jfranck@1313: // fist pass. jfranck@1313: jfranck@1313: // Queue a pass that will replace Attribute.Placeholders jfranck@1313: // with Attribute.Compound (made from synthesized containers). jfranck@1313: ctx.annotateRepeated(new Annotate.Annotator() { jfranck@1313: @Override jfranck@1313: public String toString() { jfranck@1445: return "repeated annotation pass of: " + sym + " in: " + sym.owner; jfranck@1313: } jfranck@1313: jfranck@1313: @Override jfranck@1313: public void enterAnnotation() { jfranck@1313: complete(ctx); jfranck@1313: } jfranck@1313: }); jfranck@1313: } jjg@1521: // Add non-repeating attributes jjg@1521: return buf.reverse(); jfranck@1313: } jfranck@1313: jfranck@1313: public Annotations reset() { jjg@1521: attributes = DECL_IN_PROGRESS; jfranck@1313: return this; jfranck@1313: } jfranck@1313: jfranck@1313: public boolean isEmpty() { jfranck@1313: return !isStarted() jfranck@1313: || pendingCompletion() jfranck@1313: || attributes.isEmpty(); jfranck@1313: } jfranck@1313: jjg@1521: public boolean isTypesEmpty() { jjg@1521: return type_attributes.isEmpty(); jjg@1521: } jjg@1521: jfranck@1313: public boolean pendingCompletion() { jjg@1521: return attributes == DECL_IN_PROGRESS; jfranck@1313: } jfranck@1313: jfranck@1313: public Annotations append(List l) { jjg@1521: attributes = filterDeclSentinels(attributes); jfranck@1313: jfranck@1313: if (l.isEmpty()) { jfranck@1313: ; // no-op jfranck@1313: } else if (attributes.isEmpty()) { jfranck@1313: attributes = l; jfranck@1313: } else { jfranck@1313: attributes = attributes.appendList(l); jfranck@1313: } jfranck@1313: return this; jfranck@1313: } jfranck@1313: jjg@1521: public Annotations appendUniqueTypes(List l) { jjg@1521: if (l.isEmpty()) { jjg@1521: ; // no-op jjg@1521: } else if (type_attributes.isEmpty()) { jjg@1521: type_attributes = l; jjg@1521: } else { jjg@1521: // TODO: in case we expect a large number of annotations, this jjg@1521: // might be inefficient. jjg@1521: for (Attribute.TypeCompound tc : l) { jjg@1521: if (!type_attributes.contains(tc)) jjg@1521: type_attributes = type_attributes.append(tc); jjg@1521: } jjg@1521: } jjg@1521: return this; jjg@1521: } jjg@1521: jfranck@1313: public Annotations prepend(List l) { jjg@1521: attributes = filterDeclSentinels(attributes); jfranck@1313: jfranck@1313: if (l.isEmpty()) { jfranck@1313: ; // no-op jfranck@1313: } else if (attributes.isEmpty()) { jfranck@1313: attributes = l; jfranck@1313: } else { jfranck@1313: attributes = attributes.prependList(l); jfranck@1313: } jfranck@1313: return this; jfranck@1313: } jfranck@1313: jjg@1521: private List filterDeclSentinels(List a) { jjg@1521: return (a == DECL_IN_PROGRESS || a == DECL_NOT_STARTED) jfranck@1313: ? List.nil() jfranck@1313: : a; jfranck@1313: } jfranck@1313: jfranck@1313: private boolean isStarted() { jjg@1521: return attributes != DECL_NOT_STARTED; jfranck@1313: } jfranck@1313: jfranck@1313: private List getPlaceholders() { jfranck@1313: List res = List.nil(); jjg@1521: for (Attribute.Compound a : filterDeclSentinels(attributes)) { jjg@1521: if (a instanceof Placeholder) { jjg@1521: res = res.prepend(a); jjg@1521: } jjg@1521: } jjg@1521: return res.reverse(); jjg@1521: } jjg@1521: jjg@1521: private List getTypePlaceholders() { jjg@1521: List res = List.nil(); jjg@1521: for (Attribute.TypeCompound a : type_attributes) { jfranck@1313: if (a instanceof Placeholder) { jfranck@1313: res = res.prepend(a); jfranck@1313: } jfranck@1313: } jfranck@1313: return res.reverse(); jfranck@1313: } jfranck@1313: jfranck@1313: /* jfranck@1313: * Replace Placeholders for repeating annotations with their containers jfranck@1313: */ jjg@1521: private void complete(Annotate.AnnotateRepeatedContext ctx) { jfranck@1313: Log log = ctx.log; jfranck@1313: Env env = ctx.env; jfranck@1313: JavaFileObject oldSource = log.useSource(env.toplevel.sourcefile); jfranck@1313: try { jjg@1521: // TODO: can we reduce duplication in the following branches? jjg@1521: if (ctx.isTypeCompound) { jjg@1521: Assert.check(!isTypesEmpty()); jfranck@1313: jjg@1521: if (isTypesEmpty()) { jjg@1521: return; jjg@1521: } jjg@1521: jjg@1521: List result = List.nil(); jjg@1521: for (Attribute.TypeCompound a : getTypeAttributes()) { jjg@1521: if (a instanceof Placeholder) { jjg@1521: @SuppressWarnings("unchecked") jjg@1521: Placeholder ph = (Placeholder) a; jjg@1521: Attribute.TypeCompound replacement = replaceOne(ph, ph.getRepeatedContext()); jjg@1521: jjg@1521: if (null != replacement) { jjg@1521: result = result.prepend(replacement); jjg@1521: } jjg@1521: } else { jjg@1521: result = result.prepend(a); jjg@1521: } jjg@1521: } jjg@1521: jjg@1521: type_attributes = result.reverse(); jjg@1521: jjg@1521: Assert.check(Annotations.this.getTypePlaceholders().isEmpty()); jjg@1521: } else { jjg@1521: Assert.check(!pendingCompletion()); jjg@1521: jjg@1521: if (isEmpty()) { jjg@1521: return; jjg@1521: } jjg@1521: jjg@1521: List result = List.nil(); jjg@1521: for (Attribute.Compound a : getDeclarationAttributes()) { jjg@1521: if (a instanceof Placeholder) { jjg@1521: @SuppressWarnings("unchecked") jjg@1521: Attribute.Compound replacement = replaceOne((Placeholder) a, ctx); jjg@1521: jjg@1521: if (null != replacement) { jjg@1521: result = result.prepend(replacement); jjg@1521: } jjg@1521: } else { jjg@1521: result = result.prepend(a); jjg@1521: } jjg@1521: } jjg@1521: jjg@1521: attributes = result.reverse(); jjg@1521: jjg@1521: Assert.check(Annotations.this.getPlaceholders().isEmpty()); jfranck@1313: } jfranck@1313: } finally { jfranck@1313: log.useSource(oldSource); jfranck@1313: } jfranck@1313: } jfranck@1313: jjg@1521: private T replaceOne(Placeholder placeholder, Annotate.AnnotateRepeatedContext ctx) { jfranck@1313: Log log = ctx.log; jfranck@1313: jfranck@1313: // Process repeated annotations jjg@1521: T validRepeated = ctx.processRepeatedAnnotations(placeholder.getPlaceholderFor(), sym); jfranck@1313: jfranck@1313: if (validRepeated != null) { jfranck@1313: // Check that the container isn't manually jfranck@1313: // present along with repeated instances of jfranck@1313: // its contained annotation. jjg@1521: ListBuffer manualContainer = ctx.annotated.get(validRepeated.type.tsym); jfranck@1313: if (manualContainer != null) { jjg@1492: log.error(ctx.pos.get(manualContainer.first()), "invalid.repeatable.annotation.repeated.and.container.present", jfranck@1313: manualContainer.first().type.tsym); jfranck@1313: } jfranck@1313: } jfranck@1313: jfranck@1313: // A null return will delete the Placeholder jfranck@1313: return validRepeated; jfranck@1313: } jfranck@1313: jjg@1521: private static class Placeholder extends Attribute.TypeCompound { jfranck@1313: jjg@1521: private final Annotate.AnnotateRepeatedContext ctx; jjg@1521: private final List placeholderFor; jjg@1521: private final Symbol on; jfranck@1313: jjg@1521: public Placeholder(Annotate.AnnotateRepeatedContext ctx, List placeholderFor, Symbol on) { jjg@1521: super(on.type, List.>nil(), jjg@1521: ctx.isTypeCompound ? jjg@1521: ((Attribute.TypeCompound)placeholderFor.head).position : jjg@1521: null); jjg@1521: this.ctx = ctx; jfranck@1313: this.placeholderFor = placeholderFor; jfranck@1313: this.on = on; jfranck@1313: } jfranck@1313: jfranck@1313: @Override jfranck@1313: public String toString() { jfranck@1313: return ""; jfranck@1313: } jfranck@1313: jjg@1521: public List getPlaceholderFor() { jfranck@1313: return placeholderFor; jfranck@1313: } jjg@1521: jjg@1521: public Annotate.AnnotateRepeatedContext getRepeatedContext() { jjg@1521: return ctx; jjg@1521: } jfranck@1313: } jfranck@1313: }