Changeset 13992


Ignore:
Timestamp:
Nov 7, 2019 9:26:27 PM (9 days ago)
Author:
jwedin
Message:

Changed GenericAnnotationNode to handle its state with a new state manager object. The state manager supports immutability which is used for ambiguous nodes. #5865

Location:
branches/dev-jw-2590/Compiler
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • branches/dev-jw-2590/Compiler/ModelicaFlatTree/src/jastadd/FlatAnnotations.jrag

    r13921 r13992  
    2828    public class FlatAnnotation extends GenericAnnotationNode<FlatAnnotation, FlatAnnotationProvider, FExp> {
    2929
    30         public static final FlatAnnotation AMBIGUOUS_ANNOTATION =  new FlatAnnotation(null, null, null, Mutability.IMMUTABLE);
    31 
    32         /** Creates a mutable flat annotation.
     30        public static final FlatAnnotation AMBIGUOUS_ANNOTATION =  new FlatAnnotation(null, null, null, true);
     31
     32        /** Creates an unambiguous flat annotation.
    3333         * See {@link GenericAnnotationNode#GenericAnnotationNode(String, AnnotationProvider, GenericAnnotationNode)}
    3434         */
     
    3838
    3939        /** Creates a flat annotation.
    40          * See {@link GenericAnnotationNode#GenericAnnotationNode(String, AnnotationProvider, GenericAnnotationNode, Mutability)}
     40         * See {@link GenericAnnotationNode#GenericAnnotationNode(String, AnnotationProvider, GenericAnnotationNode, boolean)}
    4141         */
    42         protected FlatAnnotation(String name, FlatAnnotationProvider node, FlatAnnotation parent, Mutability mutability) {
    43             super(name, node, parent, mutability);
     42        protected FlatAnnotation(String name, FlatAnnotationProvider node, FlatAnnotation parent, boolean ambiguous) {
     43            super(name, node, parent, ambiguous);
    4444        }
    4545
  • branches/dev-jw-2590/Compiler/ModelicaFrontEnd/src/jastadd/util/SrcAnnotations.jrag

    r13921 r13992  
    106106    public class SrcAnnotationNode extends GenericAnnotationNode<SrcAnnotationNode, SrcAnnotationProvider, SrcExp> {
    107107       
    108         public static final SrcAnnotationNode AMBIGUOUS_ANNOTATION =  new SrcAnnotationNode(null, null, null, SrcAnnotationNode.defaultEvaluator(), Mutability.IMMUTABLE);
     108        public static final SrcAnnotationNode AMBIGUOUS_ANNOTATION =  new SrcAnnotationNode(null, null, null, SrcAnnotationNode.defaultEvaluator(), true);
    109109       
    110110        private Evaluator<SrcExp> evaluator;
    111111
    112         /** Creates a mutable source annotation.
     112        /** Creates an unambiguous source annotation.
    113113         * See {@link GenericAnnotationNode#GenericAnnotationNode(String, AnnotationProvider, GenericAnnotationNode)}
    114114         */
     
    120120
    121121        /** Creates a source annotation.
    122          * See {@link GenericAnnotationNode#GenericAnnotationNode(String, AnnotationProvider, GenericAnnotationNode, Mutability)}
     122         * See {@link GenericAnnotationNode#GenericAnnotationNode(String, AnnotationProvider, GenericAnnotationNode, boolean)}
    123123         */
    124         protected SrcAnnotationNode(String name, SrcAnnotationProvider node, SrcAnnotationNode parent, Evaluator<SrcExp> evaluator, Mutability mutability) {
    125             super(name, node, parent, mutability);
     124        protected SrcAnnotationNode(String name, SrcAnnotationProvider node, SrcAnnotationNode parent, Evaluator<SrcExp> evaluator, boolean ambiguous) {
     125            super(name, node, parent, ambiguous);
    126126            this.evaluator = evaluator;
    127127        }
  • branches/dev-jw-2590/Compiler/ModelicaFrontEnd/src/java/org/jmodelica/util/annotations/GenericAnnotationNode.java

    r13936 r13992  
    2525import java.util.Map;
    2626
     27import org.jmodelica.common.URIResolver;
    2728import org.jmodelica.common.URIResolver.URIException;
    2829import org.jmodelica.util.Criteria;
    29 import org.jmodelica.util.annotations.AnnotationProvider.SubAnnotationPair;
    3030import org.jmodelica.util.collections.FilteredIterable;
    3131import org.jmodelica.util.values.ConstValue;
     
    5151        N extends AnnotationProvider<N, V>, V extends Evaluable> {
    5252
    53     public enum Mutability {
    54         MUTABLE, IMMUTABLE,
    55     }
    5653    /**
    5754     * Vendor name.
    5855     */
    5956    public static final String VENDOR_NAME = "__Modelon";
    60 
    61     private String name;
    62     private N node;
    63     private final T parent;
    64 
    65     private Collection<T> subNodes_cache;
    66     private Map<String, T> subNodesNameMap_cache;
    67     private boolean nodeWasSet = false;
    68     private boolean subNodeNodeWasSet = false;
    69     private T valueAnnotation_cache;
    70     private boolean valueAnnotation_cacheComputed;
    71     private Mutability mutability;
    72 
    73     /**
    74      * Constructor to create a mutable annotation node.<br>
     57    private final AnnotationStateManager state;
     58
     59    /**
     60     * Constructor to create an unambiguous annotation node.<br>
    7561     * <code>name</code> may be null, some nodes simply do not have a name.<br>
    7662     * <code>node</code> may only be null for the instances returned by
     
    8268     */
    8369    protected GenericAnnotationNode(String name, N node, T parent) {
    84         this(name, node, parent, Mutability.MUTABLE);
     70        this(name, node, parent, false);
    8571    }
    8672
    8773    /**
    8874     * Constructor to create an annotation node.<br>
     75     * An ambiguous node created with this constructor is immutable.
     76     * That is, calling a state changing method on an unambiguous will not have any effect.
     77     * No state change will occur nor will any exception be thrown.
    8978     * <code>name</code> may be null, some nodes simply do not have a name.<br>
    9079     * <code>node</code> may only be null for the instances returned by
     
    9483     * @param node The node that this annotation node represent.
    9584     * @param parent The parent of the node
    96      * @param mutability The mutability of the node, i.e., if it is mutable or immutable.
    97      */
    98     protected GenericAnnotationNode(String name, N node, T parent, Mutability mutability) {
    99         this.mutability = mutability;
    100         this.parent = parent;
    101         if (mutability == Mutability.MUTABLE) {
    102             setNode(name, node);
    103         }
    104         else {
    105             subNodes_cache = Collections.unmodifiableList(new ArrayList<T>());
    106             subNodesNameMap_cache = Collections.unmodifiableMap(new HashMap<String, T>());
    107             valueAnnotation_cache = self();
    108             valueAnnotation_cacheComputed = true;
    109             this.name = name;
    110         }
    111     }
    112 
    113     public boolean isImmutable() {
    114         return mutability == Mutability.IMMUTABLE;
     85     * @param ambiguous The ambiguity of the node.
     86     */
     87    protected GenericAnnotationNode(String name, N node, T parent, boolean ambiguous) {
     88        state = ambiguous ?
     89                new AmbiguousAnnotationStateManager(name, node, parent) :
     90                new MutableAnnotationStateManager(name, node, parent);
    11591    }
    11692
     
    11995     */
    12096    private void computeSubNodesCache() {
    121         if (isSubNodesCacheFresh()) {
    122             return;
    123         }
    124         if (subNodes_cache == null) {
    125             subNodes_cache = Collections.emptyList();
    126         }
    127         if (subNodesNameMap_cache == null) {
    128             subNodesNameMap_cache = Collections.emptyMap();
    129         }
    130 
    131         if(!nodeExists() || isAmbiguous()) {
    132             return;
    133         }
    134 
    135         List<T> subNodes = new ArrayList<T>();
    136         Map<String, List<T>> oldNodesMap = constructMapIncludingAmbiguous(subNodes_cache);
    137         Map<String, T> subNodesNameMap = new HashMap<String, T>();
    138         for (SubAnnotationPair<N> subNodePair : node.annotationSubNodes()) {
    139             String subNodeName = subNodePair.getAnnotationName();
    140             N subNodeNode = subNodePair.getAnnotationValue();
    141             T subNode = null;
    142            
    143             List<T> oldNodes = oldNodesMap.get(subNodeName);
    144             if (oldNodes != null) {
    145                 subNode = oldNodes.remove(0);
    146                 if (oldNodes.isEmpty()) {
    147                     oldNodesMap.remove(subNodeName);
    148                 }
    149             }
    150             createOrSetSubNodeAndAddToCaches(subNode, subNodeName, subNodeNode, subNodes, subNodesNameMap);
    151         }
    152 
    153         for(List<T> oldNodes : oldNodesMap.values()) {
    154             for(T oldNode: oldNodes) {
    155                 //TODO move the setting of the node field in the sub node to null to an explicit step
    156                 createOrSetSubNodeAndAddToCaches(oldNode, oldNode.name(), null, subNodes, subNodesNameMap);
    157             }
    158         }
    159         subNodes_cache = subNodes;
    160         subNodesNameMap_cache = subNodesNameMap;
    161         clearNodeWasSetFlags();
    162     }
    163 
    164     private T createOrSetSubNodeAndAddToCaches(T subNode, String subNodeName, N subNodeNode, Collection<T> subNodes, Map<String, T> subNodesNameMap) {
    165         if (subNode == null) {
    166             subNode = createNode(subNodeName, subNodeNode);
    167         } else {
    168             asGeneric(subNode).setNode(subNodeName, subNodeNode);
    169         }
    170         if (subNode != null) {
    171             addToCaches(subNodes, subNodesNameMap, subNode);
    172         }
    173         return subNode;
    174     }
    175 
    176     private void addToCaches(Collection<T> subNodes, Map<String, T> subNodesNameMap, T subNode) {
    177         subNodes.add(subNode);
    178         updateSubNodesNameMapCache(subNodesNameMap, subNode);
    179     }
    180 
    181     private void updateSubNodesNameMapCache(Map<String, T> subNodesNameMap, T subNode) {
    182         T previous = subNodesNameMap.put(subNode.name(), subNode);
    183         if (previous != null && previous.hasNode() && subNode.hasNode()) { // subNode goes from not ambiguous to ambiguous
    184             subNodesNameMap.put(subNode.name(), ambiguousNode());
    185         }
    186     }
    187 
    188     private T removeFromSubNodesNameMapCache(T subNode, Collection<T> subNodes, Map<String, T> subNodesNameMap) {
    189         T previous = subNodesNameMap.get(subNode.name());
    190         if (previous != null) {
    191             if (previous == ambiguousNode()) {
    192                 Map<String, List<T>> oldNodesMap = constructMapIncludingAmbiguous(filterExists(subNodes));
    193                 List<T> oldNodes = oldNodesMap.get(subNode.name());
    194                 if(oldNodes.size() == 2) { // subNode goes from ambiguous to not ambiguous
    195                     oldNodes.remove(subNode);
    196                     subNodesNameMap.put(subNode.name(), oldNodes.get(0));
    197                 }
    198             } else {
    199                 subNodesNameMap.remove(subNode.name());
    200             }
    201         }
    202         return previous;
    203     }
    204 
    205     /**
    206      * Computes a map that maps between name and node, the list is needed since
    207      * there may be multiple nodes with the same name, if that is the case,
    208      * then they are put in the list in the order that they are found!
    209      */
    210     private Map<String, List<T>> constructMapIncludingAmbiguous(Iterable<T> nodes) {
    211         if (nodes == null) {
    212             return Collections.emptyMap();
    213         }
    214         Map<String, List<T>> res = new LinkedHashMap<String, List<T>>();
    215         for (T node : nodes) {
    216             String name = node.name();
    217             List<T> withSameName = res.get(name);
    218             if (withSameName == null) {
    219                 withSameName = new ArrayList<T>(Collections.singletonList(node));
    220                 res.put(name, withSameName);
    221             } else if (withSameName.size() == 1) {
    222                 withSameName = new ArrayList<T>(withSameName);
    223                 withSameName.add(node);
    224                 res.put(name, withSameName);
    225             } else {
    226                 withSameName.add(node);
    227             }
    228         }
    229         return res;
     97        state.computeSubNodesCache();
     98    }
     99
     100    protected boolean isSubNodesCacheFresh() {
     101        return state.isSubNodesCacheFresh();
     102    }
     103
     104    protected boolean hasSubNodesCache() {
     105        return state.hasSubNodesCache();
     106    }
     107    /**
     108     * Sets the name and node of this GenericAnnotationNode. This is an internal method,
     109     * call {{@link #updateNode(String, AnnotationProvider)} instead.
     110     * @param newName The new name
     111     * @param node The new node
     112     */
     113    private void setNode(String newName, N node) {
     114        state.setNode(newName, node);
     115    }
     116
     117    private void setSubNodeNodeWasSet() {
     118        state.setSubNodeNodeWasSet();
    230119    }
    231120
     
    238127     */
    239128    private void updateSubNode(String newName, N newNode, T subNode) {
    240         removeFromSubNodesNameMapCache(subNode, subNodes_cache, subNodesNameMap_cache);
    241         asGeneric(subNode).setNode(newName, newNode);
    242         updateSubNodesNameMapCache(subNodesNameMap_cache, subNode);
    243     }
    244 
    245     protected boolean isSubNodesCacheFresh() {
    246         return !nodeWasSet && !subNodeNodeWasSet;
    247     }
    248 
    249     protected boolean hasSubNodesCache() {
    250         return subNodes_cache != null;
     129        state.updateSubNode(newName, newNode, subNode);
     130    }
     131
     132    private N createNodeForChild(GenericAnnotationNode<T, N, V> child) throws AnnotationEditException {
     133        return state.createNodeForChild(child);
    251134    }
    252135
     
    263146
    264147    T forPath(String[] paths, int currentIndex) {
    265         if (isImmutable() || isAmbiguous() || currentIndex == paths.length) {
    266             return self();
    267         }
    268         computeSubNodesCache();
    269         T subNode = subNodesNameMap_cache.get(paths[currentIndex]);
    270         if (subNode == null) {
    271             makeEmptySubNodesCacheMutable();
    272             subNode = createOrSetSubNodeAndAddToCaches(subNode, paths[currentIndex], null, subNodes_cache,
    273                     subNodesNameMap_cache);
    274         }
    275         return subNode.forPath(paths, currentIndex + 1);
    276     }
    277 
    278     private void makeEmptySubNodesCacheMutable() {
    279         if (subNodesNameMap_cache.isEmpty()) {
    280             subNodesNameMap_cache = new HashMap<String, T>();
    281         }
    282         if (subNodes_cache.isEmpty()) {
    283             subNodes_cache = new ArrayList<T>();
    284         }
    285     }
    286    
     148        return state.forPath(paths, currentIndex);
     149    }
     150
    287151    /**
    288152     * Returns reference to it self, but with correct type! This pattern
     
    313177     */
    314178    protected abstract T createNode(N node);
    315    
    316     private N createNodeForChild(GenericAnnotationNode<T, N, V> child) throws AnnotationEditException {
    317         if (child == valueAnnotation_cache) {
    318             // Trying to set the value annotation child
    319             throw new AnnotationEditException(this, "Not possible to set assign annotation value as annotation yet");
    320         }
    321         N res = node().addAnnotationSubNode(child.name());
    322         if (res == null) {
    323             throw new AnnotationEditException(child, "Unable to create sub node");
    324         }
    325         return res;
    326     }
    327 
    328     /**
    329      * Method for checking if this node has sub-nodes.
    330      *
    331      * @return true if this node has sub nodes, otherwise false.
    332      */
    333     private boolean hasSubNodes() {
    334         if (!isImmutable()) {
    335             computeSubNodesCache();
    336         }
    337         return !subNodes_cache.isEmpty();
    338     }
    339179
    340180    /**
     
    344184     */
    345185    public Iterable<T> subNodes() {
    346         if (!isImmutable()) {
    347             computeSubNodesCache();
    348         }
    349         return filterExists(subNodes_cache);
    350     }
    351 
    352     private Iterable<T> filterExists(Iterable<T> nodes) {
    353         return new FilteredIterable<T>(nodes, new Criteria<T>() {
    354 
    355             @Override
    356             public boolean test(T elem) {
    357                 return elem.nodeExists();
    358             }
    359 
    360         });
     186        return state.subNodes();
    361187    }
    362188
     
    368194     */
    369195    public N node() throws AnnotationEditException {
    370         if (!hasNode() && !isImmutable()) {
    371             if (parent == null) {
    372                 // This is a null pattern node without hope of creating
    373                 return null;
    374             }
    375             N node = asGeneric(parent()).createNodeForChild(this);
    376             updateNode(name(), node);
    377         }
    378         return node;
     196        return state.node();
    379197    }
    380198
     
    387205     */
    388206    protected N peekNode() {
    389         return node;
     207        return state.peekNode();
    390208    }
    391209
     
    397215     */
    398216    public String resolveURI(String str) throws URIException {
    399         if (node == null) {
    400             if (parent == null) {
    401                 return null; // We did our best, we cant do more
    402             }
    403             return parent.resolveURI(str);
    404         }
    405         return node.resolveURI(str);
     217        return state.resolveURI(str);
    406218    }
    407219   
     
    411223     */
    412224    public String name() {
    413         return name;
    414     }
    415 
    416     /**
    417      * Ensures that this node is mutable.
    418      * @throws UnsupportedOperationException if this node is immutable
    419      */
    420     private void verifyMutability() {
    421         if (isImmutable()) {
    422             throw new UnsupportedOperationException();
    423         }
     225        return state.name();
    424226    }
    425227
     
    431233    @SuppressWarnings("unchecked")
    432234    protected void updateNode(String newName, N node) {
    433         verifyMutability();
    434         if (parent() != null && !name().equals(newName)) {
    435             asGeneric(parent()).updateSubNode(newName, node, (T) this);
    436         } else {
    437             setNode(newName, node);
    438         }
    439     }
    440 
    441     /**
    442      * Sets the name and node of this GenericAnnotationNode. This is an internal method,
    443      * call {{@link #updateNode(String, AnnotationProvider)} instead.
    444      * @param newName The new name
    445      * @param node The new node
    446      */
    447     private void setNode(String newName, N node) {
    448         // This is an internal method because it does not update the caches in the parent node.
    449         // it needs to be protected to be accessible from the parent node.
    450         disconnectFromNode();
    451         this.name = newName;
    452         this.node = node;
    453         setNodeWasSetFlags();
    454     }
    455 
    456     /**
    457      *
     235       state.updateNode(newName, node);
     236    }
     237
     238    /**
     239     *
    458240     * @return true if this node has a value, otherwise false
    459241     */
     
    467249     */
    468250    public V value() {
    469         if (!nodeExists() || isAmbiguous() || isImmutable()) {
    470             return null;
    471         }
    472         return node().annotationValue();
     251        return state.value();
    473252    }
    474253
     
    482261     */
    483262    public void setValue(V newValue) throws AnnotationEditException {
    484         verifyMutability();
    485         try {
    486             node().setAnnotationValue(newValue);
    487         } catch (FailedToSetAnnotationValueException e) {
    488             throw new AnnotationEditException(this, e);
    489         }
     263        state.setValue(newValue);
    490264    }
    491265
     
    498272     */
    499273    public T valueAsAnnotation() {
    500         if (!valueAnnotation_cacheComputed) {
    501             verifyMutability();
    502             valueAnnotation_cacheComputed = true;
    503             if (isAmbiguous()) {
    504                 valueAnnotation_cache = ambiguousNode();
    505             } else if (nodeExists()) {
    506                 N annotationNode = valueAsProvider();
    507                 if (hasValue() && annotationNode == null) {
    508                     valueAnnotation_cache = null;
    509                 } else {
    510                     valueAnnotation_cache = createNode(annotationNode);
    511                 }
    512             } else {
    513                 valueAnnotation_cache = null;
    514             }
    515         }
    516         return valueAnnotation_cache;
     274        return state.valueAsAnnotation();
    517275    }
    518276
     
    530288        StringBuilder sb = new StringBuilder();
    531289        try {
    532             toString(sb);
     290            state.toString(sb);
    533291        } catch (IOException e) {
    534292            // Not possible, sb.append() does not throw IOException...
     
    538296
    539297    public void toString(Appendable out) throws IOException {
    540         if (name() != null) {
    541             out.append(name());
    542         }
    543         if (hasSubNodes()) {
    544             boolean first = true;
    545             for (T subNode : subNodes()) {
    546                 if (first) {
    547                     out.append('(');
    548                 }
    549                 if (!first) {
    550                     out.append(", ");
    551                 }
    552                 first = false;
    553                 subNode.toString(out);
    554             }
    555             if (!first) {
    556                 out.append(')');
    557             }
    558         }
    559         if (hasValue()) {
    560             out.append('=');
    561             out.append(value().toString());
    562         }
     298        state.toString(out);
    563299    }
    564300
     
    576312     */
    577313    protected boolean hasNode() {
    578         return isAmbiguous() || node != null;
     314        return state.hasNode();
    579315    }
    580316
     
    584320     */
    585321    public boolean nodeExists() {
    586         if (parent() != null && !parent.isImmutable()) {
    587             asGeneric(parent()).computeSubNodesCache();
    588         }
    589         return hasNode();
     322        return state.nodeExists();
    590323    }
    591324
     
    604337     */
    605338    public boolean isEach() {
    606         return node().isEach();
     339        return state.isEach();
    607340    }
    608341
     
    613346     */
    614347    public boolean isFinal() {
    615         return node().isFinal();
     348        return state.isFinal();
    616349    }
    617350
     
    622355     */
    623356    protected T parent() {
    624         return parent;
     357        return state.parent();
    625358    }
    626359
     
    933666     */
    934667    protected void disconnectFromNode() {
    935         verifyMutability();
    936         if (parent != null) {
    937             node = null;
     668        state.disconnectFromNode();
     669    }
     670
     671    public abstract class AnnotationStateManager {
     672
     673        protected abstract void computeSubNodesCache();
     674
     675        protected abstract boolean isFinal();
     676
     677        protected abstract boolean isEach();
     678
     679        protected abstract boolean isSubNodesCacheFresh();
     680
     681        protected abstract boolean hasSubNodesCache();
     682
     683        protected abstract void setNode(String newName, N node);
     684
     685        protected abstract void setSubNodeNodeWasSet();
     686
     687        protected abstract void updateSubNode(String newName, N newNode, T subNode);
     688
     689        protected abstract N createNodeForChild(GenericAnnotationNode<T,N,V> child);
     690
     691        protected abstract T forPath(String[] path, int i);
     692
     693        protected abstract Iterable<T> subNodes();
     694
     695        protected abstract N node();
     696
     697        protected abstract String resolveURI(String str) throws URIResolver.URIException ;
     698
     699        protected abstract N peekNode();
     700
     701        protected abstract String name();
     702
     703        protected abstract void disconnectFromNode();
     704
     705        protected abstract void updateNode(String newName, N node);
     706
     707        protected abstract V value();
     708
     709        protected abstract void setValue(V newValue);
     710
     711        protected abstract T valueAsAnnotation();
     712
     713        protected abstract void toString(Appendable out) throws IOException ;
     714
     715        protected abstract boolean hasNode();
     716
     717        protected abstract boolean nodeExists();
     718
     719        protected abstract T parent();
     720    }
     721
     722    public class AmbiguousAnnotationStateManager extends AnnotationStateManager {
     723
     724        private final String name;
     725        private final N node;
     726        private final T parent;
     727
     728        protected AmbiguousAnnotationStateManager(String name, N node, T parent) {
     729            this.name = name;
     730            this.node = node;
     731            this.parent = parent;
     732        }
     733
     734        @Override
     735        protected void computeSubNodesCache() {
     736            // Do nothing
     737        }
     738
     739        @Override
     740        protected boolean isSubNodesCacheFresh() {
     741            return true;
     742        }
     743
     744        @Override
     745        protected boolean hasSubNodesCache() {
     746            return true;
     747        }
     748
     749        @Override
     750        protected void setNode(String newName, N node) {
     751            // Do nothing
     752        }
     753
     754        @Override
     755        protected void setSubNodeNodeWasSet() {
     756            // Do nothing
     757        }
     758
     759        @Override
     760        protected void updateSubNode(String newName, N newNode, T subNode) {
     761            // Do nothing
     762        }
     763
     764        @Override
     765        protected N createNodeForChild(GenericAnnotationNode<T, N, V> child) {
     766            return null;
     767        }
     768
     769        @Override
     770        protected T forPath(String[] path, int i) {
     771            return self();
     772        }
     773
     774        @Override
     775        protected Iterable<T> subNodes() {
     776            return Collections.emptyList();
     777        }
     778
     779        @Override
     780        protected N node() {
     781            return node;
     782        }
     783
     784        @Override
     785        protected String resolveURI(String str) throws URIException {
     786            return null;
     787        }
     788
     789        @Override
     790        protected N peekNode() {
     791            return node;
     792        }
     793
     794        @Override
     795        protected String name() {
     796            return name;
     797        }
     798
     799        @Override
     800        protected void disconnectFromNode() {
     801            // Do nothing
     802        }
     803
     804        @Override
     805        protected void updateNode(String newName, N node) {
     806            // Do nothing
     807        }
     808
     809        @Override
     810        protected V value() {
     811            return node().annotationValue();
     812        }
     813
     814        @Override
     815        protected void setValue(V newValue) {
     816            // Do nothing
     817        }
     818
     819        @Override
     820        protected T valueAsAnnotation() {
     821            return null;
     822        }
     823
     824        @Override
     825        protected void toString(Appendable out) throws IOException {
     826            // We don't have anything to append
     827        }
     828
     829        @Override
     830        protected boolean hasNode() {
     831            return true;
     832        }
     833
     834        @Override
     835        protected boolean nodeExists() {
     836            return true;
     837        }
     838
     839        @Override
     840        protected T parent() {
     841            return parent;
     842        }
     843
     844        @Override
     845        protected boolean isFinal() {
     846            return false;
     847        }
     848
     849        @Override
     850        protected boolean isEach() {
     851            return false;
     852        }
     853    }
     854
     855    public class MutableAnnotationStateManager extends AnnotationStateManager {
     856
     857        private String name;
     858        private N node;
     859        private final T parent;
     860
     861        private Collection<T> subNodes_cache;
     862        private Map<String, T> subNodesNameMap_cache;
     863        private boolean nodeWasSet = false;
     864        private boolean subNodeNodeWasSet = false;
     865        private T valueAnnotation_cache;
     866        private boolean valueAnnotation_cacheComputed = false;
     867
     868        protected MutableAnnotationStateManager(String name, N node, T parent) {
     869            this.parent = parent;
     870            setNode(name, node);
     871        }
     872
     873        @Override
     874        protected void computeSubNodesCache() {
     875            if (isSubNodesCacheFresh()) {
     876                return;
     877            }
     878            if (subNodes_cache == null) {
     879                subNodes_cache = Collections.emptyList();
     880            }
     881            if (subNodesNameMap_cache == null) {
     882                subNodesNameMap_cache = Collections.emptyMap();
     883            }
     884
     885            if(!nodeExists() || isAmbiguous()) {
     886                return;
     887            }
     888
     889            List<T> subNodes = new ArrayList<T>();
     890            Map<String, List<T>> oldNodesMap = constructMapIncludingAmbiguous(subNodes_cache);
     891            Map<String, T> subNodesNameMap = new HashMap<String, T>();
     892            for (AnnotationProvider.SubAnnotationPair<N> subNodePair : node.annotationSubNodes()) {
     893                String subNodeName = subNodePair.getAnnotationName();
     894                N subNodeNode = subNodePair.getAnnotationValue();
     895                T subNode = null;
     896
     897                List<T> oldNodes = oldNodesMap.get(subNodeName);
     898                if (oldNodes != null) {
     899                    subNode = oldNodes.remove(0);
     900                    if (oldNodes.isEmpty()) {
     901                        oldNodesMap.remove(subNodeName);
     902                    }
     903                }
     904                createOrSetSubNodeAndAddToCaches(subNode, subNodeName, subNodeNode, subNodes, subNodesNameMap);
     905            }
     906
     907            for(List<T> oldNodes : oldNodesMap.values()) {
     908                for(T oldNode: oldNodes) {
     909                    //TODO move the setting of the node field in the sub node to null to an explicit step
     910                    createOrSetSubNodeAndAddToCaches(oldNode, oldNode.name(), null, subNodes, subNodesNameMap);
     911                }
     912            }
     913            subNodes_cache = subNodes;
     914            subNodesNameMap_cache = subNodesNameMap;
     915            clearNodeWasSetFlags();
     916        }
     917
     918        private T createOrSetSubNodeAndAddToCaches(T subNode, String subNodeName, N subNodeNode, Collection<T> subNodes, Map<String, T> subNodesNameMap) {
     919            if (subNode == null) {
     920                subNode = createNode(subNodeName, subNodeNode);
     921            } else {
     922                asGeneric(subNode).setNode(subNodeName, subNodeNode);
     923            }
     924            if (subNode != null) {
     925                addToCaches(subNodes, subNodesNameMap, subNode);
     926            }
     927            return subNode;
     928        }
     929
     930        private void addToCaches(Collection<T> subNodes, Map<String, T> subNodesNameMap, T subNode) {
     931            subNodes.add(subNode);
     932            updateSubNodesNameMapCache(subNodesNameMap, subNode);
     933        }
     934
     935        private void updateSubNodesNameMapCache(Map<String, T> subNodesNameMap, T subNode) {
     936            T previous = subNodesNameMap.put(subNode.name(), subNode);
     937            if (previous != null && previous.hasNode() && subNode.hasNode()) { // subNode goes from not ambiguous to ambiguous
     938                subNodesNameMap.put(subNode.name(), ambiguousNode());
     939            }
     940        }
     941
     942        private T removeFromSubNodesNameMapCache(T subNode, Collection<T> subNodes, Map<String, T> subNodesNameMap) {
     943            T previous = subNodesNameMap.get(subNode.name());
     944            if (previous != null) {
     945                if (previous == ambiguousNode()) {
     946                    Map<String, List<T>> oldNodesMap = constructMapIncludingAmbiguous(filterExists(subNodes));
     947                    List<T> oldNodes = oldNodesMap.get(subNode.name());
     948                    if(oldNodes.size() == 2) { // subNode goes from ambiguous to not ambiguous
     949                        oldNodes.remove(subNode);
     950                        subNodesNameMap.put(subNode.name(), oldNodes.get(0));
     951                    }
     952                } else {
     953                    subNodesNameMap.remove(subNode.name());
     954                }
     955            }
     956            return previous;
     957        }
     958
     959        /**
     960         * Computes a map that maps between name and node, the list is needed since
     961         * there may be multiple nodes with the same name, if that is the case,
     962         * then they are put in the list in the order that they are found!
     963         */
     964        private Map<String, List<T>> constructMapIncludingAmbiguous(Iterable<T> nodes) {
     965            if (nodes == null) {
     966                return Collections.emptyMap();
     967            }
     968            Map<String, List<T>> res = new LinkedHashMap<String, List<T>>();
     969            for (T node : nodes) {
     970                String name = node.name();
     971                List<T> withSameName = res.get(name);
     972                if (withSameName == null) {
     973                    withSameName = new ArrayList<T>(Collections.singletonList(node));
     974                    res.put(name, withSameName);
     975                } else if (withSameName.size() == 1) {
     976                    withSameName = new ArrayList<T>(withSameName);
     977                    withSameName.add(node);
     978                    res.put(name, withSameName);
     979                } else {
     980                    withSameName.add(node);
     981                }
     982            }
     983            return res;
     984        }
     985
     986        @Override
     987        protected void updateSubNode(String newName, N newNode, T subNode) {
     988            removeFromSubNodesNameMapCache(subNode, subNodes_cache, subNodesNameMap_cache);
     989            asGeneric(subNode).setNode(newName, newNode);
     990            updateSubNodesNameMapCache(subNodesNameMap_cache, subNode);
     991        }
     992
     993        @Override
     994        protected boolean isSubNodesCacheFresh() {
     995            return !nodeWasSet && !subNodeNodeWasSet;
     996        }
     997
     998        @Override
     999        protected boolean hasSubNodesCache() {
     1000            return subNodes_cache != null;
     1001        }
     1002
     1003        @Override
     1004        protected T forPath(String[] paths, int currentIndex) {
     1005            if (isAmbiguous() || currentIndex == paths.length) {
     1006                return self();
     1007            }
     1008            computeSubNodesCache();
     1009            T subNode = subNodesNameMap_cache.get(paths[currentIndex]);
     1010            if (subNode == null) {
     1011                makeEmptySubNodesCacheMutable();
     1012                subNode = createOrSetSubNodeAndAddToCaches(subNode, paths[currentIndex], null, subNodes_cache,
     1013                        subNodesNameMap_cache);
     1014            }
     1015            return subNode.forPath(paths, currentIndex + 1);
     1016        }
     1017
     1018        private void makeEmptySubNodesCacheMutable() {
     1019            if (subNodesNameMap_cache.isEmpty()) {
     1020                subNodesNameMap_cache = new HashMap<String, T>();
     1021            }
     1022            if (subNodes_cache.isEmpty()) {
     1023                subNodes_cache = new ArrayList<T>();
     1024            }
     1025        }
     1026
     1027        @Override
     1028        protected N createNodeForChild(GenericAnnotationNode<T, N, V> child) throws AnnotationEditException {
     1029            if (child == valueAnnotation_cache) {
     1030                // Trying to set the value annotation child
     1031                throw new AnnotationEditException(GenericAnnotationNode.this, "Not possible to set assign annotation value as annotation yet");
     1032            }
     1033            N res = node().addAnnotationSubNode(child.name());
     1034            if (res == null) {
     1035                throw new AnnotationEditException(child, "Unable to create sub node");
     1036            }
     1037            return res;
     1038        }
     1039
     1040        private boolean hasSubNodes() {
     1041            computeSubNodesCache();
     1042            return !subNodes_cache.isEmpty();
     1043        }
     1044
     1045        @Override
     1046        protected Iterable<T> subNodes() {
     1047            computeSubNodesCache();
     1048            return filterExists(subNodes_cache);
     1049        }
     1050
     1051        private Iterable<T> filterExists(Iterable<T> nodes) {
     1052            return new FilteredIterable<T>(nodes, new Criteria<T>() {
     1053
     1054                @Override
     1055                public boolean test(T elem) {
     1056                    return elem.nodeExists();
     1057                }
     1058
     1059            });
     1060        }
     1061
     1062        @Override
     1063        protected N node() throws AnnotationEditException {
     1064            if (!hasNode()) {
     1065                if (parent == null) {
     1066                    // This is a null pattern node without hope of creating
     1067                    return null;
     1068                }
     1069                N node = asGeneric(parent()).createNodeForChild(GenericAnnotationNode.this);
     1070                updateNode(name(), node);
     1071            }
     1072            return node;
     1073        }
     1074
     1075        @Override
     1076        protected N peekNode() {
     1077            return node;
     1078        }
     1079
     1080        @Override
     1081        protected String resolveURI(String str) throws URIResolver.URIException {
     1082            if (node == null) {
     1083                if (parent == null) {
     1084                    return null; // We did our best, we cant do more
     1085                }
     1086                return parent.resolveURI(str);
     1087            }
     1088            return node.resolveURI(str);
     1089        }
     1090
     1091        @Override
     1092        protected String name() {
     1093            return name;
     1094        }
     1095
     1096        @SuppressWarnings("unchecked")
     1097        @Override
     1098        protected void updateNode(String newName, N node) {
     1099            if (parent() != null && !name().equals(newName)) {
     1100                asGeneric(parent()).updateSubNode(newName, node, (T) GenericAnnotationNode.this);
     1101            } else {
     1102                setNode(newName, node);
     1103            }
     1104        }
     1105
     1106        @Override
     1107        protected void setNode(String newName, N node) {
     1108            // This is an internal method because it does not update the caches in the parent node.
     1109            // it needs to be protected to be accessible from the parent node.
     1110            disconnectFromNode();
     1111            this.name = newName;
     1112            this.node = node;
    9381113            setNodeWasSetFlags();
    9391114        }
    940         if (subNodes_cache != null) {
    941             for (T t : subNodes_cache) {
    942                 t.disconnectFromNode();
    943             }
    944         }
    945     }
    946 
    947     private void setNodeWasSetFlags() {
    948         nodeWasSet = true;
    949         if (parent() != null) {
    950             asGeneric(parent()).setSubNodeNodeWasSet();
    951         }
    952     }
    953 
    954     private void setSubNodeNodeWasSet() {
    955         subNodeNodeWasSet = true;
    956     }
    957 
    958     private void clearNodeWasSetFlags() {
    959         nodeWasSet = false;
    960         subNodeNodeWasSet = false;
     1115
     1116        @Override
     1117        protected V value() {
     1118            if (!nodeExists() || isAmbiguous()) {
     1119                return null;
     1120            }
     1121            return node().annotationValue();
     1122        }
     1123
     1124        @Override
     1125        protected void setValue(V newValue) throws AnnotationEditException {
     1126            try {
     1127                node().setAnnotationValue(newValue);
     1128            } catch (FailedToSetAnnotationValueException e) {
     1129                throw new AnnotationEditException(GenericAnnotationNode.this, e);
     1130            }
     1131        }
     1132
     1133        @Override
     1134        protected T valueAsAnnotation() {
     1135            if (!valueAnnotation_cacheComputed) {
     1136                valueAnnotation_cacheComputed = true;
     1137                if (isAmbiguous()) {
     1138                    valueAnnotation_cache = ambiguousNode();
     1139                } else if (nodeExists()) {
     1140                    N annotationNode = valueAsProvider();
     1141                    if (hasValue() && annotationNode == null) {
     1142                        valueAnnotation_cache = null;
     1143                    } else {
     1144                        valueAnnotation_cache = createNode(annotationNode);
     1145                    }
     1146                } else {
     1147                    valueAnnotation_cache = null;
     1148                }
     1149            }
     1150            return valueAnnotation_cache;
     1151        }
     1152
     1153        @Override
     1154        protected void toString(Appendable out) throws IOException {
     1155            if (name() != null) {
     1156                out.append(name());
     1157            }
     1158            if (hasSubNodes()) {
     1159                boolean first = true;
     1160                for (T subNode : subNodes()) {
     1161                    if (first) {
     1162                        out.append('(');
     1163                    }
     1164                    if (!first) {
     1165                        out.append(", ");
     1166                    }
     1167                    first = false;
     1168                    subNode.toString(out);
     1169                }
     1170                if (!first) {
     1171                    out.append(')');
     1172                }
     1173            }
     1174            if (hasValue()) {
     1175                out.append('=');
     1176                out.append(value().toString());
     1177            }
     1178        }
     1179
     1180        @Override
     1181        protected boolean hasNode() {
     1182            return isAmbiguous() || node != null;
     1183        }
     1184
     1185        @Override
     1186        protected boolean nodeExists() {
     1187            if (parent() != null) {
     1188                asGeneric(parent()).computeSubNodesCache();
     1189            }
     1190            return hasNode();
     1191        }
     1192
     1193        @Override
     1194        protected T parent() {
     1195            return parent;
     1196        }
     1197
     1198        @Override
     1199        protected void disconnectFromNode() {
     1200            if (parent != null) {
     1201                node = null;
     1202                setNodeWasSetFlags();
     1203            }
     1204            if (subNodes_cache != null) {
     1205                for (T t : subNodes_cache) {
     1206                    t.disconnectFromNode();
     1207                }
     1208            }
     1209        }
     1210
     1211        private void setNodeWasSetFlags() {
     1212            nodeWasSet = true;
     1213            if (parent() != null) {
     1214                asGeneric(parent()).setSubNodeNodeWasSet();
     1215            }
     1216        }
     1217
     1218        @Override
     1219        protected void setSubNodeNodeWasSet() {
     1220            subNodeNodeWasSet = true;
     1221        }
     1222
     1223        private void clearNodeWasSetFlags() {
     1224            nodeWasSet = false;
     1225            subNodeNodeWasSet = false;
     1226        }
     1227
     1228        @Override
     1229        protected boolean isFinal() {
     1230            return node.isFinal();
     1231        }
     1232
     1233        @Override
     1234        protected boolean isEach() {
     1235            return node.isEach();
     1236        }
     1237
    9611238    }
    9621239
  • branches/dev-jw-2590/Compiler/ModelicaFrontEnd/test/junit/org/jmodelica/util/annotations/mock/DummyAnnotationNode.java

    r13921 r13992  
    77public class DummyAnnotationNode extends GenericAnnotationNode<DummyAnnotationNode, DummyAnnotProvider, Evaluable> {
    88
    9     private static DummyAnnotationNode ambiguousNode = new DummyAnnotationNode(null, null, null, Mutability.IMMUTABLE);
     9    private static DummyAnnotationNode ambiguousNode = new DummyAnnotationNode(null, null, null, true);
    1010
    11     /** Creates a mutable dummy annotation.
     11    /** Creates an unambiguous dummy annotation.
    1212     * See {@link GenericAnnotationNode#GenericAnnotationNode(String, AnnotationProvider, GenericAnnotationNode)}
    1313     */
     
    1717
    1818    /** Creates a dummy annotation.
    19      * See {@link GenericAnnotationNode#GenericAnnotationNode(String, AnnotationProvider, GenericAnnotationNode, Mutability)}
     19     * See {@link GenericAnnotationNode#GenericAnnotationNode(String, AnnotationProvider, GenericAnnotationNode, boolean)}
    2020     */
    21     public DummyAnnotationNode(String name, DummyAnnotProvider node, DummyAnnotationNode parent, Mutability mutability) {
    22         super(name, node, parent, mutability);
     21    public DummyAnnotationNode(String name, DummyAnnotProvider node, DummyAnnotationNode parent, boolean ambiguous) {
     22        super(name, node, parent, ambiguous);
    2323    }
    2424
Note: See TracChangeset for help on using the changeset viewer.