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.graph.query.parse; 25 26 import java.util.Collection; 27 import java.util.Collections; 28 import java.util.HashSet; 29 import java.util.Set; 30 import java.util.concurrent.ConcurrentHashMap; 31 import java.util.concurrent.ConcurrentMap; 32 import net.jcip.annotations.ThreadSafe; 33 import org.modeshape.common.text.ParsingException; 34 import org.modeshape.common.util.CheckArg; 35 import org.modeshape.graph.GraphI18n; 36 import org.modeshape.graph.query.model.QueryCommand; 37 import org.modeshape.graph.query.model.TypeSystem; 38 39 /** 40 * A thread-safe collection of {@link QueryParser} implementations that can be used to parse queries by language. 41 */ 42 @ThreadSafe 43 public class QueryParsers { 44 45 private final ConcurrentMap<String, QueryParser> parsers = new ConcurrentHashMap<String, QueryParser>(); 46 47 /** 48 * Create a new collection of the supplied {@link QueryParser} objects. 49 * 50 * @param parsers the parsers 51 */ 52 public QueryParsers( QueryParser... parsers ) { 53 if (parsers != null) { 54 for (QueryParser parser : parsers) { 55 if (parser != null) addLanguage(parser); 56 } 57 } 58 } 59 60 /** 61 * Create a new collection of the supplied {@link QueryParser} objects. 62 * 63 * @param parsers the parsers 64 */ 65 public QueryParsers( Iterable<QueryParser> parsers ) { 66 if (parsers != null) addLanguages(parsers); 67 } 68 69 /** 70 * Add a language to this engine by supplying its parser. 71 * 72 * @param languageParser the query parser for the language 73 * @throws IllegalArgumentException if the language parser is null 74 */ 75 public void addLanguage( QueryParser languageParser ) { 76 CheckArg.isNotNull(languageParser, "languageParser"); 77 this.parsers.put(languageParser.getLanguage().trim().toLowerCase(), languageParser); 78 } 79 80 /** 81 * Add one or more languages to this engine by supplying the corresponding parsers. 82 * 83 * @param firstLanguage the query parser for the first language 84 * @param additionalLanguages the query parsers for the additional languages 85 * @throws IllegalArgumentException if the language parser is null 86 */ 87 public void addLanguages( QueryParser firstLanguage, 88 QueryParser... additionalLanguages ) { 89 addLanguage(firstLanguage); 90 for (QueryParser language : additionalLanguages) { 91 addLanguage(language); 92 } 93 } 94 95 /** 96 * Add one or more languages to this engine by supplying the corresponding parsers. 97 * 98 * @param languages the query parsers for the languages that are to be added 99 * @throws IllegalArgumentException if the iterable reference is null 100 */ 101 public void addLanguages( Iterable<QueryParser> languages ) { 102 CheckArg.isNotNull(languages, "languages"); 103 for (QueryParser language : languages) { 104 addLanguage(language); 105 } 106 } 107 108 /** 109 * Remove from this engine the language with the given name. 110 * 111 * @param language the name of the language, which is to match the {@link QueryParser#getLanguage() language} of the parser 112 * @return the parser for the language, or null if the engine had no support for the named language 113 * @throws IllegalArgumentException if the language is null 114 */ 115 public QueryParser removeLanguage( String language ) { 116 CheckArg.isNotNull(language, "language"); 117 return this.parsers.remove(language.toLowerCase()); 118 } 119 120 /** 121 * Remove from this engine the language with the given name. 122 * 123 * @param firstLanguage the name of the first language to remove, which must match the {@link QueryParser#getLanguage() 124 * language} of the parser 125 * @param additionalLanguages the names of the additional languages to remove, which must match the 126 * {@link QueryParser#getLanguage() language} of the parser 127 * @return the parser for the language, or null if the engine had no support for the named language 128 * @throws IllegalArgumentException if the language is null 129 */ 130 public Collection<QueryParser> removeLanguages( String firstLanguage, 131 String... additionalLanguages ) { 132 Collection<QueryParser> removed = new HashSet<QueryParser>(); 133 QueryParser parser = removeLanguage(firstLanguage); 134 if (parser != null) removed.add(parser); 135 for (String language : additionalLanguages) { 136 parser = removeLanguage(language); 137 if (parser != null) removed.add(parser); 138 } 139 return removed; 140 } 141 142 /** 143 * Get the set of languages that this engine is capable of parsing. 144 * 145 * @return the unmodifiable copy of the set of languages; never null but possibly empty 146 */ 147 public Set<String> getLanguages() { 148 Set<String> result = new HashSet<String>(); 149 for (QueryParser parser : parsers.values()) { 150 result.add(parser.getLanguage()); 151 } 152 return Collections.unmodifiableSet(result); 153 } 154 155 /** 156 * Get the parser for the supplied language. 157 * 158 * @param language the language in which the query is expressed; must case-insensitively match one of the supported 159 * {@link #getLanguages() languages} 160 * @return the query parser, or null if the supplied language is not supported 161 * @throws IllegalArgumentException if the language is null 162 */ 163 public QueryParser getParserFor( String language ) { 164 CheckArg.isNotNull(language, "language"); 165 return parsers.get(language.trim().toLowerCase()); 166 } 167 168 /** 169 * Execute the supplied query by planning, optimizing, and then processing it. 170 * 171 * @param typeSystem the type system that should be used 172 * @param language the language in which the query is expressed; must case-insensitively match one of the supported 173 * {@link #getLanguages() languages} 174 * @param query the query that is to be executed 175 * @return the parsed query command; never null 176 * @throws IllegalArgumentException if the language, context or query references are null, or if the language is not known 177 * @throws ParsingException if there is an error parsing the supplied query 178 * @throws InvalidQueryException if the supplied query can be parsed but is invalid 179 */ 180 public QueryCommand parse( TypeSystem typeSystem, 181 String language, 182 String query ) { 183 CheckArg.isNotNull(language, "language"); 184 CheckArg.isNotNull(typeSystem, "typeSystem"); 185 CheckArg.isNotNull(query, "query"); 186 QueryParser parser = parsers.get(language.trim().toLowerCase()); 187 if (parser == null) { 188 throw new IllegalArgumentException(GraphI18n.unknownQueryLanguage.text(language)); 189 } 190 return parser.parseQuery(query, typeSystem); 191 } 192 193 /** 194 * {@inheritDoc} 195 * 196 * @see java.lang.Object#hashCode() 197 */ 198 @Override 199 public int hashCode() { 200 // There won't be too many instances of QueryParsers, so have them all return the same hash code 201 return 1; 202 } 203 204 /** 205 * {@inheritDoc} 206 * 207 * @see java.lang.Object#equals(java.lang.Object) 208 */ 209 @Override 210 public boolean equals( Object obj ) { 211 if (obj == this) return true; 212 if (obj instanceof QueryParsers) { 213 QueryParsers that = (QueryParsers)obj; 214 return this.getLanguages().equals(that.getLanguages()); 215 } 216 return false; 217 } 218 219 /** 220 * {@inheritDoc} 221 * 222 * @see java.lang.Object#toString() 223 */ 224 @Override 225 public String toString() { 226 return "Query parsers: " + parsers.keySet(); 227 } 228 }