| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.solr.rest.schema; |
| |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.apache.solr.common.SolrException; |
| import org.apache.solr.common.SolrException.ErrorCode; |
| import org.apache.solr.common.params.CommonParams; |
| import org.apache.solr.schema.IndexSchema; |
| import org.apache.solr.schema.SimilarityFactory; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| |
| /** |
| * Utility class for converting a JSON definition of a FieldType into the |
| * XML format expected by the FieldTypePluginLoader. |
| */ |
| public class FieldTypeXmlAdapter { |
| |
| public static Node toNode(Map<String,?> json) { |
| DocumentBuilder docBuilder; |
| try { |
| docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| } catch (ParserConfigurationException e) { |
| throw new SolrException(ErrorCode.SERVER_ERROR, e); |
| } |
| |
| Document doc = docBuilder.newDocument(); |
| Element fieldType = doc.createElement(IndexSchema.FIELD_TYPE); |
| appendAttrs(fieldType, json); |
| |
| // transform the analyzer definitions into XML elements |
| Element analyzer = transformAnalyzer(doc, json, "analyzer", null); |
| if (analyzer != null) |
| fieldType.appendChild(analyzer); |
| |
| analyzer = transformAnalyzer(doc, json, "indexAnalyzer", "index"); |
| if (analyzer != null) |
| fieldType.appendChild(analyzer); |
| |
| analyzer = transformAnalyzer(doc, json, "queryAnalyzer", "query"); |
| if (analyzer != null) |
| fieldType.appendChild(analyzer); |
| |
| analyzer = transformAnalyzer(doc, json, "multiTermAnalyzer", "multiterm"); |
| if (analyzer != null) |
| fieldType.appendChild(analyzer); |
| |
| Element similarity = transformSimilarity(doc, json, "similarity"); |
| if (similarity != null) |
| fieldType.appendChild(similarity); |
| |
| return fieldType; |
| } |
| |
| @SuppressWarnings("unchecked") |
| protected static Element transformSimilarity(Document doc, Map<String,?> json, String jsonFieldName) { |
| Object jsonField = json.get(jsonFieldName); |
| if (jsonField == null) |
| return null; // it's ok for this field to not exist in the JSON map |
| |
| if (!(jsonField instanceof Map)) |
| throw new SolrException(ErrorCode.BAD_REQUEST, "Invalid fieldType definition! Expected JSON object for "+ |
| jsonFieldName+" not a "+jsonField.getClass().getName()); |
| |
| Element similarity = doc.createElement("similarity"); |
| Map<String,?> config = (Map<String,?>)jsonField; |
| similarity.setAttribute(SimilarityFactory.CLASS_NAME, (String)config.remove(SimilarityFactory.CLASS_NAME)); |
| for (Map.Entry<String,?> entry : config.entrySet()) { |
| Object val = entry.getValue(); |
| if (val != null) { |
| Element child = doc.createElement(classToXmlTag(val.getClass())); |
| child.setAttribute(CommonParams.NAME, entry.getKey()); |
| child.setTextContent(entry.getValue().toString()); |
| similarity.appendChild(child); |
| } |
| } |
| return similarity; |
| } |
| |
| /** Convert types produced by noggit's ObjectBuilder (Boolean, Double, Long, String) to plugin param XML tags. */ |
| protected static String classToXmlTag(Class<?> clazz) { |
| switch (clazz.getSimpleName()) { |
| case "Boolean": return "bool"; |
| case "Double": return "double"; |
| case "Long": return "long"; |
| case "String": return "str"; |
| } |
| throw new SolrException(ErrorCode.BAD_REQUEST, "Unsupported object type '" + clazz.getSimpleName() + "'"); |
| } |
| |
| @SuppressWarnings("unchecked") |
| protected static Element transformAnalyzer(Document doc, Map<String,?> json, String jsonFieldName, String analyzerType) { |
| Object jsonField = json.get(jsonFieldName); |
| if (jsonField == null) |
| return null; // it's ok for this field to not exist in the JSON map |
| |
| if (!(jsonField instanceof Map)) |
| throw new SolrException(ErrorCode.BAD_REQUEST, "Invalid fieldType definition! Expected JSON object for "+ |
| jsonFieldName+" not a "+jsonField.getClass().getName()); |
| |
| return createAnalyzerElement(doc, analyzerType, (Map<String,?>)jsonField); |
| } |
| |
| @SuppressWarnings("unchecked") |
| protected static Element createAnalyzerElement(Document doc, String type, Map<String,?> analyzer) { |
| Element analyzerElem = appendAttrs(doc.createElement("analyzer"), analyzer); |
| if (type != null) |
| analyzerElem.setAttribute("type", type); |
| |
| List<Map<String,?>> charFilters = (List<Map<String,?>>)analyzer.get("charFilters"); |
| Map<String,?> tokenizer = (Map<String,?>)analyzer.get("tokenizer"); |
| List<Map<String,?>> filters = (List<Map<String,?>>)analyzer.get("filters"); |
| |
| if (analyzer.get("class") == null) { |
| if (charFilters != null) |
| appendFilterElements(doc, analyzerElem, "charFilter", charFilters); |
| |
| if (tokenizer == null) |
| throw new SolrException(ErrorCode.BAD_REQUEST, "Analyzer must define a tokenizer!"); |
| |
| if (tokenizer.get("class") == null) |
| throw new SolrException(ErrorCode.BAD_REQUEST, "Every tokenizer must define a class property!"); |
| |
| analyzerElem.appendChild(appendAttrs(doc.createElement("tokenizer"), tokenizer)); |
| |
| if (filters != null) |
| appendFilterElements(doc, analyzerElem, "filter", filters); |
| |
| } else { // When analyzer class is specified: char filters, tokenizers, and filters are disallowed |
| if (charFilters != null) |
| throw new SolrException |
| (ErrorCode.BAD_REQUEST, "An analyzer with a class property may not define any char filters!"); |
| |
| if (tokenizer != null) |
| throw new SolrException |
| (ErrorCode.BAD_REQUEST, "An analyzer with a class property may not define a tokenizer!"); |
| |
| if (filters != null) |
| throw new SolrException |
| (ErrorCode.BAD_REQUEST, "An analyzer with a class property may not define any filters!"); |
| } |
| |
| return analyzerElem; |
| } |
| |
| protected static void appendFilterElements(Document doc, Element analyzer, String filterName, List<Map<String,?>> filters) { |
| for (Map<String,?> next : filters) { |
| String filterClass = (String)next.get("class"); |
| if (filterClass == null) |
| throw new SolrException(ErrorCode.BAD_REQUEST, |
| "Every "+filterName+" must define a class property!"); |
| analyzer.appendChild(appendAttrs(doc.createElement(filterName), next)); |
| } |
| } |
| |
| protected static Element appendAttrs(Element elm, Map<String,?> json) { |
| for (Map.Entry<String,?> entry : json.entrySet()) { |
| Object val = entry.getValue(); |
| if (val != null && !(val instanceof Map)) |
| elm.setAttribute(entry.getKey(), val.toString()); |
| } |
| return elm; |
| } |
| } |