jfranck@1313: /* jfranck@1313: * Copyright (c) 2012, 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: * jfranck@1313: * NOT_STARTED indicates that the Symbol this instance belongs to have 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 jfranck@1313: * it directly. You can also move back to the IN_PROGRESS sate 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: jfranck@1313: private static final List NOT_STARTED = List.of(null); jfranck@1313: private static final List IN_PROGRESS = List.of(null); jfranck@1313: /* jfranck@1313: * This field should never be null jfranck@1313: */ jfranck@1313: private List attributes = NOT_STARTED; jfranck@1313: /* jfranck@1313: * The Symbol this Annotatios belong to jfranck@1313: */ jfranck@1313: private final Symbol s; jfranck@1313: jfranck@1313: public Annotations(Symbol s) { jfranck@1313: this.s = s; jfranck@1313: } jfranck@1313: jfranck@1313: public List getAttributes() { jfranck@1313: return filterSentinels(attributes); jfranck@1313: } jfranck@1313: jfranck@1313: public void setAttributes(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: jfranck@1313: public void setAttributes(Annotations other) { jfranck@1313: if (other == null) { jfranck@1313: throw new NullPointerException(); jfranck@1313: } jfranck@1313: setAttributes(other.getAttributes()); jfranck@1313: } jfranck@1313: jfranck@1313: public void setAttributesWithCompletion(final Annotate.AnnotateRepeatedContext ctx) { jfranck@1313: Assert.check(pendingCompletion() || (!isStarted() && s.kind == PCK)); jfranck@1313: jfranck@1313: Map> annotated = ctx.annotated; jfranck@1313: boolean atLeastOneRepeated = false; jfranck@1313: List buf = List.nil(); jfranck@1313: for (ListBuffer lb : annotated.values()) { jfranck@1313: if (lb.size() == 1) { jfranck@1313: buf = buf.prepend(lb.first()); jfranck@1313: } else { // repeated jfranck@1313: buf = buf.prepend(new Placeholder(lb.toList(), s)); jfranck@1313: atLeastOneRepeated = true; jfranck@1313: } jfranck@1313: } jfranck@1313: jfranck@1313: // Add non-repeating attributes jfranck@1313: setAttributes(buf.reverse()); 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 jfranck@1313: // guarantee that the @ContainedBy 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 jfranck@1313: // finished. This will work because @ContainedBy 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: jfranck@1313: @Override jfranck@1313: public String toString() { jfranck@1313: return "repeated annotation pass of: " + s + " in: " + s.owner; jfranck@1313: } jfranck@1313: jfranck@1313: @Override jfranck@1313: public void enterAnnotation() { jfranck@1313: complete(ctx); jfranck@1313: } jfranck@1313: }); jfranck@1313: } jfranck@1313: } jfranck@1313: jfranck@1313: public Annotations reset() { jfranck@1313: attributes = 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: jfranck@1313: public boolean pendingCompletion() { jfranck@1313: return attributes == IN_PROGRESS; jfranck@1313: } jfranck@1313: jfranck@1313: public Annotations append(List l) { jfranck@1313: attributes = filterSentinels(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: jfranck@1313: public Annotations prepend(List l) { jfranck@1313: attributes = filterSentinels(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: jfranck@1313: private List filterSentinels(List a) { jfranck@1313: return (a == IN_PROGRESS || a == NOT_STARTED) jfranck@1313: ? List.nil() jfranck@1313: : a; jfranck@1313: } jfranck@1313: jfranck@1313: private boolean isStarted() { jfranck@1313: return attributes != NOT_STARTED; jfranck@1313: } jfranck@1313: jfranck@1313: private List getPlaceholders() { jfranck@1313: List res = List.nil(); jfranck@1313: for (Attribute.Compound a : filterSentinels(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: */ jfranck@1313: private void complete(Annotate.AnnotateRepeatedContext ctx) { jfranck@1313: Assert.check(!pendingCompletion()); jfranck@1313: Log log = ctx.log; jfranck@1313: Env env = ctx.env; jfranck@1313: JavaFileObject oldSource = log.useSource(env.toplevel.sourcefile); jfranck@1313: try { jfranck@1313: jfranck@1313: if (isEmpty()) { jfranck@1313: return; jfranck@1313: } jfranck@1313: jfranck@1313: List result = List.nil(); jfranck@1313: for (Attribute.Compound a : getAttributes()) { jfranck@1313: if (a instanceof Placeholder) { jfranck@1313: Attribute.Compound replacement = replaceOne((Placeholder) a, ctx); jfranck@1313: jfranck@1313: if (null != replacement) { jfranck@1313: result = result.prepend(replacement); jfranck@1313: } jfranck@1313: } else { jfranck@1313: result = result.prepend(a); jfranck@1313: } jfranck@1313: } jfranck@1313: jfranck@1313: attributes = result.reverse(); jfranck@1313: jfranck@1313: Assert.check(Annotations.this.getPlaceholders().isEmpty()); jfranck@1313: } finally { jfranck@1313: log.useSource(oldSource); jfranck@1313: } jfranck@1313: } jfranck@1313: jfranck@1313: private Attribute.Compound replaceOne(Placeholder placeholder, Annotate.AnnotateRepeatedContext ctx) { jfranck@1313: Log log = ctx.log; jfranck@1313: jfranck@1313: // Process repeated annotations jfranck@1313: Attribute.Compound validRepeated = jfranck@1313: ctx.processRepeatedAnnotations(placeholder.getPlaceholderFor()); 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. jfranck@1313: ListBuffer manualContainer = ctx.annotated.get(validRepeated.type.tsym); jfranck@1313: if (manualContainer != null) { jfranck@1313: log.error(ctx.pos.get(manualContainer.first()), "invalid.containedby.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: } jfranck@1313: jfranck@1313: private static class Placeholder extends Attribute.Compound { jfranck@1313: jfranck@1313: private List placeholderFor; jfranck@1313: private Symbol on; jfranck@1313: jfranck@1313: public Placeholder(List placeholderFor, Symbol on) { jfranck@1313: super(Type.noType, List.>nil()); 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: jfranck@1313: public List getPlaceholderFor() { jfranck@1313: return placeholderFor; jfranck@1313: } jfranck@1313: } jfranck@1313: }