View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.sequencer.teiid;
25  
26  import java.io.FileWriter;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collection;
30  import java.util.List;
31  import org.modeshape.common.collection.Problems;
32  import org.modeshape.common.collection.SimpleProblems;
33  import org.modeshape.common.i18n.I18n;
34  import org.modeshape.graph.ExecutionContext;
35  import org.modeshape.graph.Graph;
36  import org.modeshape.graph.Location;
37  import org.modeshape.graph.Subgraph;
38  import org.modeshape.graph.SubgraphNode;
39  import org.modeshape.graph.connector.inmemory.InMemoryRepositorySource;
40  import org.modeshape.graph.property.Name;
41  import org.modeshape.graph.property.NamespaceRegistry;
42  import org.modeshape.graph.property.NamespaceRegistry.Namespace;
43  import org.modeshape.graph.property.basic.LocalNamespaceRegistry;
44  import com.beust.jcommander.JCommander;
45  import com.beust.jcommander.Parameter;
46  import com.google.common.collect.ArrayListMultimap;
47  import com.google.common.collect.Multimap;
48  
49  /**
50   * Utility class to generate a CND file from an ECore model.
51   */
52  public class CndFromEcore {
53  
54      public static void main( String[] args ) {
55          CndFromEcore converter = new CndFromEcore();
56          JCommander commander = new JCommander(converter, args);
57          if (!converter.isValid()) {
58              commander.usage();
59          } else {
60              converter.execute();
61              if (converter.getProblems().hasProblems()) {
62                  System.out.println(converter.getProblems());
63              }
64          }
65      }
66  
67      private static final char NEWLINE = '\n';
68  
69      @Parameter( description = "Comma-separated list of Ecore input file paths or URLs" )
70      private List<String> ecoreFileNames = new ArrayList<String>();
71      @Parameter( names = {"-o", "-out"}, description = "Name of the CND output file" )
72      private String cndFileName;
73      @Parameter( names = "-debug", description = "Debug mode" )
74      private boolean debug = false;
75      @Parameter( names = "-mixin", description = "EClasses are converted to node types" )
76      private boolean mixins = false;
77      @Parameter( names = "-shortNames", description = "Generate shorter names where possible" )
78      private boolean shortNames = false;
79  
80      private Problems problems = new SimpleProblems();
81  
82      /**
83       * Get the names of the Ecore files that are to be processed into node types.
84       * 
85       * @return the Ecore file names
86       */
87      public List<String> getEcoreFileNames() {
88          return ecoreFileNames;
89      }
90  
91      /**
92       * @param ecoreFileNames Sets ecoreFileNames to the specified value.
93       */
94      public void setEcoreFileNames( List<String> ecoreFileNames ) {
95          this.ecoreFileNames = ecoreFileNames;
96      }
97  
98      /**
99       * @param ecoreFileNames Sets ecoreFileNames to the specified value.
100      */
101     public void setEcoreFileNames( String... ecoreFileNames ) {
102         this.ecoreFileNames = new ArrayList<String>(Arrays.asList(ecoreFileNames));
103     }
104 
105     /**
106      * @return cndFileName
107      */
108     public String getCndFileName() {
109         return cndFileName;
110     }
111 
112     /**
113      * @param cndFileName Sets cndFileName to the specified value.
114      */
115     public void setCndFileName( String cndFileName ) {
116         this.cndFileName = cndFileName;
117     }
118 
119     /**
120      * @return debug
121      */
122     public boolean isDebug() {
123         return debug;
124     }
125 
126     /**
127      * @param debug Sets debug to the specified value.
128      */
129     public void setDebug( boolean debug ) {
130         this.debug = debug;
131     }
132 
133     /**
134      * @return mixins
135      */
136     public boolean generatesMixins() {
137         return mixins;
138     }
139 
140     /**
141      * @param mixins Sets mixins to the specified value.
142      */
143     public void setGeneratesMixins( boolean mixins ) {
144         this.mixins = mixins;
145     }
146 
147     /**
148      * @return shortNames
149      */
150     public boolean generateShortNames() {
151         return shortNames;
152     }
153 
154     /**
155      * @param shortNames Sets shortNames to the specified value.
156      */
157     public void setGeneratesShortNames( boolean shortNames ) {
158         this.shortNames = shortNames;
159     }
160 
161     public boolean isValid() {
162         if (ecoreFileNames.isEmpty()) return false;
163         return true;
164     }
165 
166     /**
167      * @return problems
168      */
169     public Problems getProblems() {
170         return problems;
171     }
172 
173     public void execute() {
174         ExecutionContext context = new ExecutionContext();
175         NamespaceRegistry registry = context.getNamespaceRegistry();
176 
177         // Use a local namespace registry so that we know which namespaces were used ...
178         LocalNamespaceRegistry localRegistry = new LocalNamespaceRegistry(registry);
179         context = context.with(localRegistry);
180 
181         // Read in each of the Ecore files ...
182         List<String> ecoreFileContributions = new ArrayList<String>();
183         for (String ecoreFileName : getEcoreFileNames()) {
184             String ecoreName = ecoreFileName.replace("\\.ecore", "");
185             debug(TeiidI18n.readingEcoreFile, ecoreFileName);
186             StringBuilder sb = new StringBuilder();
187             try {
188                 InMemoryRepositorySource source = new InMemoryRepositorySource();
189                 source.setName(ecoreName);
190                 Graph graph = Graph.create(source, context);
191                 graph.importXmlFrom(ecoreFileName).into("/"); // file path or URL or even on classpath
192                 Subgraph subgraph = graph.getSubgraphOfDepth(20).at("/ecore:EPackage");
193                 CndGraphReader reader = new CndGraphReader(subgraph, generatesMixins(), generateShortNames());
194                 reader.writeTo(sb);
195 
196                 ecoreFileContributions.add(sb.toString());
197             } catch (Throwable t) {
198                 problems.addError(TeiidI18n.errorReadingEcoreFile, ecoreFileName, t.getLocalizedMessage());
199             }
200         }
201 
202         // Create the output file ...
203         StringBuilder output = new StringBuilder();
204         // Write the header first ...
205         output.append(getHeader());
206 
207         // Write the namespaces that were used ...
208         for (Namespace namespace : localRegistry.getLocalNamespaces()) {
209             write(output, namespace);
210         }
211         output.append(NEWLINE);
212 
213         // And add in the CND contribution from each file ...
214         for (String contribution : ecoreFileContributions) {
215             output.append(contribution);
216             output.append(NEWLINE);
217         }
218 
219         // Now write it to the file ...
220         if (cndFileName != null && cndFileName.trim().length() != 0) {
221             try {
222                 FileWriter writer = new FileWriter(cndFileName);
223                 try {
224                 } finally {
225                     writer.close();
226                 }
227             } catch (Throwable t) {
228                 problems.addError(TeiidI18n.errorWritingCndFile, cndFileName, t.getLocalizedMessage());
229             }
230         } else {
231             System.out.println(output);
232         }
233     }
234 
235     protected void write( StringBuilder writer,
236                           Namespace namespace ) {
237         writer.append("<")
238               .append(namespace.getPrefix())
239               .append("='")
240               .append(namespace.getNamespaceUri())
241               .append("'>")
242               .append(NEWLINE);
243     }
244 
245     protected String getHeader() {
246         StringBuilder sb = new StringBuilder();
247         sb.append("/*").append(NEWLINE);
248         sb.append(" * Generated using the ").append(getClass().getCanonicalName()).append(" program.").append(NEWLINE);
249         sb.append(" *").append(NEWLINE);
250         sb.append(" */").append(NEWLINE);
251         return sb.toString();
252     }
253 
254     protected void debug( I18n msg,
255                           Object... params ) {
256         if (isDebug()) {
257             System.out.println(msg.text(params));
258         }
259     }
260 
261     protected static class CndGraphReader extends XmiGraphReader {
262         private final boolean generateMixins;
263 
264         protected CndGraphReader( Subgraph subgraph,
265                                   boolean generateMixins,
266                                   boolean generateShortNames ) {
267             super(subgraph, generateShortNames);
268             this.generateMixins = generateMixins;
269         }
270 
271         protected void writeTo( StringBuilder sb ) {
272             SubgraphNode pkg = subgraph.getRoot();
273             String pkgName = inflector.titleCase(firstValue(pkg, "name"));
274             String uri = firstValue(pkg, "nsURI");
275             String prefix = namespacePrefix(firstValue(pkg, "nsPrefix"));
276 
277             // Add the CND output from this file to the complete output ...
278             sb.append("// -------------------------------------------").append(NEWLINE);
279             sb.append("// ").append(pkgName).append(NEWLINE);
280             sb.append("// -------------------------------------------").append(NEWLINE);
281 
282             // Register the namespace ...
283             namespaces.register(prefix, uri);
284             setCurrentNamespaceUri(uri);
285 
286             // Look for EEnums ...
287             Multimap<Name, String> literalsByEnumName = ArrayListMultimap.create();
288             for (Location child : pkg.getChildren()) {
289                 SubgraphNode classifier = pkg.getNode(child.getPath().getLastSegment());
290                 String type = firstValue(classifier, "xsi:type"); // e.g., 'ecore:EClass' or 'ecore:EEnum'
291                 if ("ecore:EEnum".equals(type)) {
292                     Name enumName = nameFrom(firstValue(classifier, "name"));
293                     for (Location feature : classifier.getChildren()) {
294                         SubgraphNode literal = classifier.getNode(feature.getPath().getLastSegment());
295                         String literalValue = firstValue(literal, "name");
296                         literalsByEnumName.put(enumName, literalValue);
297                     }
298                 }
299             }
300 
301             for (Location child : pkg.getChildren()) {
302                 // Classifier ...
303                 SubgraphNode classifier = pkg.getNode(child.getPath().getLastSegment());
304                 String type = firstValue(classifier, "xsi:type"); // e.g., 'ecore:EClass' or 'ecore:EEnum'
305                 if ("ecore:EEnum".equals(type)) continue;
306 
307                 Name nodeTypeName = nameFrom(firstValue(classifier, "name"));
308                 boolean isAbstract = firstValue(classifier, "abstract", false);
309                 List<Name> supertypes = names(classifier, "eSuperTypes", "\\s");
310 
311                 // Write out the CND node type ...
312                 sb.append("[").append(stringFrom(nodeTypeName)).append("] ");
313 
314                 // Write out the CND supertypes ...
315                 if (!supertypes.isEmpty()) {
316                     sb.append("> ");
317                     boolean first = true;
318                     for (Name supertype : supertypes) {
319                         if (first) first = false;
320                         else sb.append(",");
321                         sb.append(stringFrom(supertype)).append(" ");
322                     }
323                 }
324                 if (isAbstract) sb.append("abstract ");
325                 if (generateMixins) sb.append("mixin ");
326 
327                 // Write out the property and child node definitions ...
328                 for (Location feature : classifier.getChildren()) {
329                     SubgraphNode structuralFeature = classifier.getNode(feature.getPath().getLastSegment());
330                     String featureType = firstValue(structuralFeature, "xsi:type"); // e.g., 'ecore:EAttribute'
331                     Name featureName = nameFrom(firstValue(structuralFeature, "name"));
332                     long upperBound = firstValue(structuralFeature, "upperBound", 1L);
333                     long lowerBound = firstValue(structuralFeature, "lowerBound", 0L);
334                     boolean isSingle = upperBound == 1;
335                     boolean isRequired = lowerBound > 0;
336                     boolean isReference = "ecore:EReference".equals(featureType);
337                     boolean isTransient = firstValue(structuralFeature, "transient", false);
338                     boolean isContainment = firstValue(structuralFeature, "containment", false);
339                     boolean isUnsettable = firstValue(structuralFeature, "unsettable", false);
340                     boolean isVolatile = firstValue(structuralFeature, "volatile", false);
341                     boolean isChangeble = firstValue(structuralFeature, "changeable", true);
342                     Name dataType = nameFrom(firstValue(structuralFeature, "eType"));
343                     String defaultValue = firstValue(structuralFeature, "defaultValueLiteral");
344 
345                     // Figure out the JCR primary type and constraint values ...
346                     String jcrType = jcrTypeNameFor(dataType);
347                     Collection<String> constraints = literalsByEnumName.get(dataType);
348                     if (!constraints.isEmpty()) {
349                         // Then the values are literals, so we do have constraints ...
350                         jcrType = "STRING";
351                     }
352 
353                     if (isContainment) {
354                         // This is a child node definition ...
355                         int x = 0;
356                     } else {
357                         // This is a property definition ...
358                         String propDefnName = stringFrom(featureName);
359                         sb.append(NEWLINE);
360                         sb.append(" - ").append(propDefnName).append(" (").append(jcrType).append(") ");
361                         if (defaultValue != null) sb.append("= '").append(defaultValue).append("' ");
362                         if (isRequired) sb.append("mandatory ");
363                         if (!isSingle) sb.append("multiple ");
364                         if (!isChangeble) sb.append("protected autocreated");
365                         if (!constraints.isEmpty()) {
366                             sb.append(NEWLINE);
367                             sb.append("   < ");
368                             boolean first = true;
369                             for (String constraint : constraints) {
370                                 if (first) first = false;
371                                 else sb.append(", ");
372                                 sb.append("'").append(constraint).append("'");
373                             }
374                         }
375                     }
376                 }
377                 sb.append(NEWLINE);
378                 sb.append(NEWLINE);
379             }
380         }
381     }
382 }