/* * Copyright (C) 2014 Jörg Prante * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses * or write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. * * The interactive user interfaces in modified source and object code * versions of this program must display Appropriate Legal Notices, * as required under Section 5 of the GNU Affero General Public License. * */ package org.xbib.elasticsearch.index.mapper.standardnumber; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexOptions; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.core.StringFieldMapper; import org.xbib.elasticsearch.common.standardnumber.StandardNumber; import java.io.IOException; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import static org.elasticsearch.index.mapper.MapperBuilders.stringField; public class StandardnumberMapper extends FieldMapper { public static final String CONTENT_TYPE = "standardnumber"; public static final class Defaults { public static final StandardnumberFieldType FIELD_TYPE = new StandardnumberFieldType(); static { FIELD_TYPE.freeze(); } } static final class StandardnumberFieldType extends MappedFieldType { protected StandardnumberFieldType() { super(); } protected StandardnumberFieldType(StandardnumberMapper.StandardnumberFieldType ref) { super(ref); } public StandardnumberMapper.StandardnumberFieldType clone() { return new StandardnumberMapper.StandardnumberFieldType(this); } @Override public String typeName() { return "standardnumber"; } public String value(Object value) { return value == null ? null : value.toString(); } } public static class Builder extends FieldMapper.Builder<Builder, StandardnumberMapper> { private StringFieldMapper.Builder contentBuilder; private StringFieldMapper.Builder stdnumBuilder = stringField("standardnumber"); private StandardnumberService service; public Builder(String name, StandardnumberService service) { super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE); this.service = service; this.contentBuilder = stringField(name); this.builder = this; } public Builder content(StringFieldMapper.Builder content) { this.contentBuilder = content; return this; } public Builder stdnum(StringFieldMapper.Builder stdnum) { this.stdnumBuilder = stdnum; return this; } @Override public StandardnumberMapper build(BuilderContext context) { MappedFieldType defaultFieldType = Defaults.FIELD_TYPE.clone(); if (this.fieldType.indexOptions() != IndexOptions.NONE && !this.fieldType.tokenized()) { defaultFieldType.setOmitNorms(true); defaultFieldType.setIndexOptions(IndexOptions.DOCS); if (!this.omitNormsSet && this.fieldType.boost() == 1.0F) { this.fieldType.setOmitNorms(true); } if (!this.indexOptionsSet) { this.fieldType.setIndexOptions(IndexOptions.DOCS); } } defaultFieldType.freeze(); this.setupFieldType(context); context.path().add(name); StringFieldMapper contentMapper = contentBuilder.build(context); StringFieldMapper stdnumMapper = stdnumBuilder.build(context); context.path().remove(); return new StandardnumberMapper(name, this.fieldType, defaultFieldType, context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo, service, contentMapper, stdnumMapper); } } public static class TypeParser implements Mapper.TypeParser { private StandardnumberService service; public void setService(StandardnumberService service) { this.service = service; } @SuppressWarnings({"unchecked","rawtypes"}) @Override public Mapper.Builder parse(String name, Map<String, Object> mapping, ParserContext parserContext) throws MapperParsingException { StandardnumberMapper.Builder builder = new Builder(name, service); Iterator<Map.Entry<String, Object>> iterator = mapping.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Object> entry = iterator.next(); String fieldName = entry.getKey(); Object fieldNode = entry.getValue(); if (fieldName.equals("fields")) { Map<String, Object> fieldsNode = (Map<String, Object>) fieldNode; for (Map.Entry<String, Object> fieldsEntry : fieldsNode.entrySet()) { String propName = fieldsEntry.getKey(); Object propNode = fieldsEntry.getValue(); if (name.equals(propName)) { builder.content((StringFieldMapper.Builder) parserContext.typeParser("string").parse(name, (Map<String, Object>) propNode, parserContext)); } else if ("standardnumber".equals(propName)) { builder.stdnum((StringFieldMapper.Builder) parserContext.typeParser("string").parse(propName, (Map<String, Object>) propNode, parserContext)); } } iterator.remove(); } } return builder; } } private final StandardnumberService service; private final FieldMapper contentMapper; private final FieldMapper stdnumMapper; public StandardnumberMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType, Settings indexSettings, MultiFields multiFields, CopyTo copyTo, StandardnumberService service, FieldMapper contentMapper, FieldMapper stdnumMapper) { super(simpleName, fieldType, defaultFieldType, indexSettings, multiFields, copyTo); this.service = service; this.contentMapper = contentMapper; this.stdnumMapper = stdnumMapper; } @Override public Mapper parse(ParseContext context) throws IOException { String content = null; XContentParser parser = context.parser(); XContentParser.Token token = parser.currentToken(); if (token == XContentParser.Token.VALUE_STRING) { content = parser.text(); } if (content == null) { return null; } context = context.createExternalValueContext(content); contentMapper.parse(context); try { Collection<StandardNumber> stdnums = service.detect(content); for (StandardNumber stdnum : stdnums) { context = context.createExternalValueContext(stdnum.normalizedValue()); stdnumMapper.parse(context); } } catch(NumberFormatException e) { context = context.createExternalValueContext("unknown"); stdnumMapper.parse(context); } return null; } @Override protected void parseCreateField(ParseContext parseContext, List<Field> fields) throws IOException { } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(simpleName()); builder.field("type", CONTENT_TYPE); builder.startObject("fields"); contentMapper.toXContent(builder, params); stdnumMapper.toXContent(builder, params); builder.endObject(); builder.endObject(); return builder; } @Override protected String contentType() { return CONTENT_TYPE; } }