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 }