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.ddl;
25  
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.LinkedList;
29  import java.util.List;
30  import net.jcip.annotations.Immutable;
31  import org.modeshape.common.text.ParsingException;
32  import org.modeshape.common.text.Position;
33  import org.modeshape.graph.JcrLexicon;
34  import org.modeshape.graph.JcrNtLexicon;
35  import org.modeshape.sequencer.ddl.dialect.derby.DerbyDdlParser;
36  import org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlParser;
37  import org.modeshape.sequencer.ddl.dialect.postgres.PostgresDdlParser;
38  import org.modeshape.sequencer.ddl.node.AstNode;
39  
40  /**
41   * A set of parsers capable of understanding DDL file content. This class can be used directly to create an {@link AstNode} tree
42   * representing nodes and properties for DDL statement components.
43   * <p>
44   * You can also provide an input or parent {@link AstNode} node as the starting point for your tree.
45   * </p>
46   * <p>
47   * The parser is based on the SQL-92 and extended by specific dialects. These dialect-specific parsers provide db-specific parsing
48   * of db-specific statements of statement extensions, features or properties.
49   * </p>
50   */
51  @Immutable
52  public class DdlParsers {
53  
54      public static final List<DdlParser> BUILTIN_PARSERS;
55  
56      static {
57          List<DdlParser> parsers = new ArrayList<DdlParser>();
58          parsers.add(new StandardDdlParser());
59          parsers.add(new OracleDdlParser());
60          parsers.add(new DerbyDdlParser());
61          parsers.add(new PostgresDdlParser());
62          BUILTIN_PARSERS = Collections.unmodifiableList(parsers);
63      }
64  
65      private List<DdlParser> parsers;
66  
67      /**
68       * Create an instance that uses all of the {@link #BUILTIN_PARSERS built-in parsers}.
69       */
70      public DdlParsers() {
71          this.parsers = BUILTIN_PARSERS;
72      }
73  
74      /**
75       * Create an instance that uses the supplied parsers, in order.
76       * 
77       * @param parsers the list of parsers; may be empty or null if the {@link #BUILTIN_PARSERS built-in parsers} should be used
78       */
79      public DdlParsers( List<DdlParser> parsers ) {
80          this.parsers = (parsers != null && !parsers.isEmpty()) ? parsers : BUILTIN_PARSERS;
81      }
82  
83      /**
84       * Parse the supplied DDL content and return the {@link AstNode root node} of the AST representation.
85       * 
86       * @param ddl content string; may not be null
87       * @param fileName the approximate name of the file containing the DDL content; may be null if this is not known
88       * @return the root tree {@link AstNode}
89       * @throws ParsingException if there is an error parsing the supplied DDL content
90       */
91      public AstNode parse( String ddl,
92                            String fileName ) throws ParsingException {
93          assert ddl != null;
94  
95          // Go through each parser and score the DDL content ...
96          List<ScoredParser> scoredParsers = new LinkedList<ScoredParser>();
97          for (DdlParser parser : parsers) {
98              DdlParserScorer scorer = new DdlParserScorer();
99              try {
100                 Object scoreResult = parser.score(ddl, fileName, scorer);
101                 scoredParsers.add(new ScoredParser(parser, scorer, scoreResult));
102             } catch (Throwable t) {
103                 // Continue ...
104             }
105         }
106 
107         if (!scoredParsers.isEmpty()) {
108             Collections.sort(scoredParsers);
109         } else {
110             // Just keep the parsers in order ...
111             for (DdlParser parser : parsers) {
112                 scoredParsers.add(new ScoredParser(parser, new DdlParserScorer(), null));
113             }
114         }
115 
116         // Go through each of the parsers (in order) to parse the content. Start with the first one
117         // and return if there is no parsing failure; otherwise, just continue with the next parser
118         // until no failure ...
119         AstNode astRoot = null;
120         RuntimeException firstException = null;
121         for (ScoredParser scoredParser : scoredParsers) {
122             try {
123                 astRoot = new AstNode(StandardDdlLexicon.STATEMENTS_CONTAINER);
124                 astRoot.setProperty(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.UNSTRUCTURED);
125                 DdlParser parser = scoredParser.getParser();
126                 parser.parse(ddl, astRoot, scoredParser.getScoringResult());
127                 astRoot.setProperty(StandardDdlLexicon.PARSER_ID, parser.getId());
128                 // Success, so return the resulting AST ...
129                 return astRoot;
130             } catch (ParsingException t) {
131                 if (firstException == null) firstException = t;
132                 // Continue ...
133             } catch (RuntimeException t) {
134                 if (firstException == null) firstException = t;
135                 // Continue ...
136             }
137         }
138         if (firstException != null) {
139             throw firstException;
140         }
141         // No exceptions, but nothing was found ...
142         throw new ParsingException(new Position(-1, 1, 0), DdlSequencerI18n.errorParsingDdlContent.text());
143     }
144 
145     protected static class ScoredParser implements Comparable<ScoredParser> {
146         private final DdlParserScorer scorer;
147         private final DdlParser parser;
148         private final Object scoringResult;
149 
150         protected ScoredParser( DdlParser parser,
151                                 DdlParserScorer scorer,
152                                 Object scoringResult ) {
153             this.parser = parser;
154             this.scorer = scorer;
155             this.scoringResult = scoringResult;
156             assert this.parser != null;
157             assert this.scorer != null;
158         }
159 
160         /**
161          * @return parser
162          */
163         public DdlParser getParser() {
164             return parser;
165         }
166 
167         /**
168          * @return scorer
169          */
170         public DdlParserScorer getScorer() {
171             return scorer;
172         }
173 
174         /**
175          * @return scoringResult
176          */
177         public Object getScoringResult() {
178             return scoringResult;
179         }
180 
181         /**
182          * {@inheritDoc}
183          * 
184          * @see java.lang.Comparable#compareTo(java.lang.Object)
185          */
186         public int compareTo( ScoredParser that ) {
187             if (that == null) return 1;
188             return that.getScorer().getScore() - this.getScorer().getScore();
189         }
190     }
191 }