View Javadoc
1   /*
2    * Copyright © 2017, Saleforce.com, Inc
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are met:
7    *     * Redistributions of source code must retain the above copyright
8    *       notice, this list of conditions and the following disclaimer.
9    *     * Redistributions in binary form must reproduce the above copyright
10   *       notice, this list of conditions and the following disclaimer in the
11   *       documentation and/or other materials provided with the distribution.
12   *     * Neither the name of the <organization> nor the
13   *       names of its contributors may be used to endorse or promote products
14   *       derived from this software without specific prior written permission.
15   *
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19   * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   */
27  package com.salesforce.apt.graph.model;
28  
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.Collections;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Set;
37  
38  import javax.lang.model.element.TypeElement;
39  
40  public class DefinitionModel extends AbstractModel {
41  
42    private final List<InstanceModel> objectDefinitions = new ArrayList<>();
43    private final List<ExpectedModel> expectedDefinitions = new ArrayList<>();
44    private final List<String> dependencyNames = new ArrayList<>();
45    
46    //because this field is transient, deserializers will not set it.  We must
47    //carefully account for this by setting the value anywhere it may be used.
48    private transient List<DefinitionModel> dependencies;
49    private final List<InstanceModel> providedInstances = new ArrayList<>();
50    private final Set<ExpectedModel> computedExpected = new HashSet<>();
51    private String sha256;          //when read from/written to file.
52    private String sourceLocation;  //when read from file
53    private final boolean rootNode;
54    
55    private final Map<String, String> dependencyNameToSha256 = new HashMap<>();  
56    
57    private transient boolean lockedSourceRead = false;                  //phase 1
58    private transient boolean lockedDefintionsMerged = false;            //phase 2
59    private transient boolean lockedAnalyzed = false;                    //phase 3
60  
61    private void failIfLockRead() {
62      if (isLockedSourceRead()) {
63        throw new IllegalStateException("Attempting to modify 'source read' content after source read is locked");
64      }
65    }
66    
67    private void failIfDefintionsMerged() {
68      lockSourceRead();
69      if (isLockedDefintionsMerged()) {
70        throw new IllegalStateException("Attempting to modify 'definition merge' content after definition merge is locked");
71      }
72    }
73    
74    private void failIfLockedAnalyzed() {
75      lockDefintionsMerged();
76      if (isLockedAnalyzed()) {
77        throw new IllegalStateException("Attempting to modify analyzed structure after structure has been computed");
78      }
79    }
80    
81    /**
82     * @return true if this is a root node, and no other nodes may depend on it.
83     */
84    public boolean isRootNode() {
85      return rootNode;
86    }
87    
88    private void setDepenendencyArrayIfNull() {
89      if (dependencies == null) {
90        dependencies = new ArrayList<>();
91      }
92    }
93    
94    public List<DefinitionModel> getDependencies() {
95      setDepenendencyArrayIfNull();
96      lockDefintionsMerged();
97      return Collections.unmodifiableList(dependencies);
98    }
99  
100   public DefinitionModel(String name) {
101     this(name, false);
102   }
103 
104   public DefinitionModel(String name, boolean rootNode) {
105     super(name);
106     this.rootNode = rootNode;
107   }
108   
109   public DefinitionModel(TypeElement type) {
110     this(type, false);
111   }
112   
113   public DefinitionModel(TypeElement type, boolean rootNode) {
114     super(type);
115     this.rootNode = rootNode;
116   }
117 
118   public void addDependency(DefinitionModel model) {
119     setDepenendencyArrayIfNull();
120     failIfDefintionsMerged();
121     this.dependencies.add(model);
122   }
123 
124   public void addDependencies(List<DefinitionModel> model) {
125     setDepenendencyArrayIfNull();
126     failIfDefintionsMerged();
127     this.dependencies.addAll(model);
128   }
129 
130   public void addDefinition(InstanceModel model) {
131     failIfLockRead();
132     this.objectDefinitions.add(model);
133   }
134 
135   public void addDefinition(ExpectedModel model) {
136     failIfLockRead();
137     this.expectedDefinitions.add(model);
138   }
139 
140   public void addDependencyNames(List<String> model) {
141     failIfLockRead();
142     this.dependencyNames.addAll(model);
143   }
144   
145   public void addDependencyNames(String dependencyName) {
146     failIfLockRead();
147     this.dependencyNames.add(dependencyName);
148   }
149 
150   public List<String> getDependencyNames() {
151     lockSourceRead();
152     return Collections.unmodifiableList(dependencyNames);
153   }
154   
155   public List<InstanceModel> getObjectDefinitions() {
156     lockSourceRead();
157     return Collections.unmodifiableList(objectDefinitions);
158   }
159 
160   public List<ExpectedModel> getExpectedDefinitions() {
161     lockSourceRead();
162     return Collections.unmodifiableList(expectedDefinitions);
163   }
164 
165   public boolean isComplete() {
166     return getExpectedDefinitions().size() == 0;
167   }
168   
169   public List<InstanceModel> getProvidedInstances() {
170     lockAnalyzed();
171     return Collections.unmodifiableList(providedInstances);
172   }
173   
174   public void addAllProvidedInstances(Collection<InstanceModel> providedInstances) {
175     failIfLockedAnalyzed();
176     this.providedInstances.addAll(providedInstances);
177   }
178 
179   public Set<ExpectedModel> getComputedExpected() {
180     lockAnalyzed();
181     return Collections.unmodifiableSet(computedExpected);
182   }
183   
184   public void addAllComputedExpected(Collection<ExpectedModel> computedExpected) {
185     failIfLockedAnalyzed();
186     this.computedExpected.addAll(computedExpected);
187   }
188   
189   public void addComputedExpected(ExpectedModel computedExpected) {
190     failIfLockedAnalyzed();
191     this.computedExpected.add(computedExpected);
192   }
193   
194   /**
195    * For the purposes of identification of the object definition class, the location of the class suffices.
196    *
197    * @return a unique identity representing this definition, specifically the fully qualified name of the source type.
198    */
199   public String getIdentity() {
200     //wont lockSourceRead(); as identity is read while attaching definitions.
201     return getElementLocation();
202   }
203 
204   /**
205    * For the purposes of identification of the object definition class, the location of the class suffices.
206    *
207    * @return the Identity of the {@link DefinitionModel}
208    */
209   public String toString() {
210     lockSourceRead();
211     return getElementLocation();
212   }
213 
214   
215   public String getSha256() {
216     lockAnalyzed();
217     return sha256;
218   }
219 
220   public void setSha256(String sha256) {
221     failIfLockedAnalyzed();
222     lockAnalyzed();
223     this.sha256 = sha256;
224   }
225 
226   public boolean isLockedSourceRead() {
227     return lockedSourceRead;
228   }
229   
230   public boolean isLockedDefintionsMerged() {
231     return lockedDefintionsMerged;
232   }
233 
234   public boolean isLockedAnalyzed() {
235     return lockedAnalyzed;
236   }
237 
238   private void lockSourceRead() {
239     lockedSourceRead = true;
240   }
241 
242   private void lockDefintionsMerged() {
243     lockSourceRead();
244     lockedDefintionsMerged = true;
245   }
246 
247   /**
248    * Public so that once all shas are write to {@link DefinitionModel#addDependencyNameToSha256} this 
249    * can be called, locking down the definition.
250    */
251   private void lockAnalyzed() {
252     lockSourceRead();
253     lockDefintionsMerged();
254     lockedAnalyzed = true;
255   }
256 
257   
258   public Map<String, String> getDependencyNameToSha256() {
259     lockDefintionsMerged();
260     return Collections.unmodifiableMap(dependencyNameToSha256);
261   }
262 
263   public void addDependencyNameToSha256(String dependencyName, String sha256) {
264     failIfLockedAnalyzed();
265     dependencyNameToSha256.put(dependencyName, sha256);
266   }
267 
268   public void addAllDependencyNameToSha256(Map<String, String>  dependencyNameToSha256) {
269     failIfLockedAnalyzed();
270     dependencyNameToSha256.putAll(dependencyNameToSha256);
271   }
272 
273   public String getSourceLocation() {
274     return sourceLocation;
275   }
276 
277   public void setSourceLocation(String sourceLocation) {
278     failIfLockRead();
279     this.sourceLocation = sourceLocation;
280   }
281 
282   public String getSourcePackage() {
283     String packageName = "";
284     if (getIdentity().lastIndexOf(".") != -1) {
285       packageName = getIdentity().substring(0, getIdentity().lastIndexOf("."));
286     }
287     return packageName;
288   }
289 
290   public String getSourceClass() {
291     return getIdentity().substring(getIdentity().lastIndexOf(".") + 1);
292   } 
293 }