001
002package org.xbib.elasticsearch.index.mapper.standardnumber;
003
004import org.elasticsearch.common.xcontent.XContentBuilder;
005import org.elasticsearch.common.xcontent.XContentParser;
006import org.elasticsearch.index.mapper.FieldMapperListener;
007import org.elasticsearch.index.mapper.Mapper;
008import org.elasticsearch.index.mapper.MapperParsingException;
009import org.elasticsearch.index.mapper.MergeContext;
010import org.elasticsearch.index.mapper.MergeMappingException;
011import org.elasticsearch.index.mapper.ObjectMapperListener;
012import org.elasticsearch.index.mapper.ParseContext;
013import org.elasticsearch.index.mapper.core.StringFieldMapper;
014import org.xbib.elasticsearch.index.analysis.standardnumber.Detector;
015import org.xbib.standardnumber.StandardNumber;
016
017import java.io.IOException;
018import java.util.Collection;
019import java.util.Map;
020
021import static org.elasticsearch.index.mapper.MapperBuilders.stringField;
022
023public class StandardNumberMapper implements Mapper {
024
025    public static final String CONTENT_TYPE = "standardnumber";
026
027    public static class Builder extends Mapper.Builder<Builder, StandardNumberMapper> {
028
029        private StringFieldMapper.Builder contentBuilder;
030
031        private StringFieldMapper.Builder stdnumBuilder = stringField("standardnumber");
032
033        private Detector detector;
034
035        public Builder(String name, Detector detector) {
036            super(name);
037            this.detector = detector;
038            this.contentBuilder = stringField(name);
039            this.builder = this;
040        }
041
042        public Builder content(StringFieldMapper.Builder content) {
043            this.contentBuilder = content;
044            return this;
045        }
046
047        public Builder stdnum(StringFieldMapper.Builder stdnum) {
048            this.stdnumBuilder = stdnum;
049            return this;
050        }
051
052        @Override
053        public StandardNumberMapper build(BuilderContext context) {
054            context.path().add(name);
055            StringFieldMapper contentMapper = contentBuilder.build(context);
056            StringFieldMapper stdnumMapper = stdnumBuilder.build(context);
057            context.path().remove();
058            return new StandardNumberMapper(name, detector, contentMapper, stdnumMapper);
059        }
060    }
061
062    public static class TypeParser implements Mapper.TypeParser {
063
064        private Detector detector;
065
066        public TypeParser(Detector detector) {
067            this.detector = detector;
068        }
069
070        @SuppressWarnings({"unchecked","rawtypes"})
071        @Override
072        public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext)
073                throws MapperParsingException {
074            StandardNumberMapper.Builder builder = new Builder(name, detector);
075            for (Map.Entry<String, Object> entry : node.entrySet()) {
076                String fieldName = entry.getKey();
077                Object fieldNode = entry.getValue();
078
079                if (fieldName.equals("fields")) {
080                    Map<String, Object> fieldsNode = (Map<String, Object>) fieldNode;
081                    for (Map.Entry<String, Object> fieldsEntry : fieldsNode.entrySet()) {
082                        String propName = fieldsEntry.getKey();
083                        Object propNode = fieldsEntry.getValue();
084
085                        if (name.equals(propName)) {
086                            builder.content((StringFieldMapper.Builder) parserContext.typeParser("string").parse(name,
087                                    (Map<String, Object>) propNode, parserContext));
088                        } else if ("standardnumber".equals(propName)) {
089                            builder.stdnum((StringFieldMapper.Builder) parserContext.typeParser("string").parse(propName,
090                                    (Map<String, Object>) propNode, parserContext));
091                        }
092                    }
093                }
094            }
095
096            return builder;
097        }
098    }
099
100    private final String name;
101    private final Detector detector;
102    private final StringFieldMapper contentMapper;
103    private final StringFieldMapper stdnumMapper;
104
105    public StandardNumberMapper(String name, Detector detector,
106                                StringFieldMapper contentMapper,
107                                StringFieldMapper stdnumMapper) {
108        this.name = name;
109        this.detector = detector;
110        this.contentMapper = contentMapper;
111        this.stdnumMapper = stdnumMapper;
112    }
113
114    @Override
115    public String name() {
116        return name;
117    }
118
119    @Override
120    public void parse(ParseContext context) throws IOException {
121        String content = null;
122
123        XContentParser parser = context.parser();
124        XContentParser.Token token = parser.currentToken();
125
126        if (token == XContentParser.Token.VALUE_STRING) {
127            content = parser.text();
128        }
129
130        context.externalValue(content);
131        contentMapper.parse(context);
132
133        try {
134            Collection<StandardNumber> stdnums = detector.detect(content);
135            for (StandardNumber stdnum : stdnums) {
136                context.externalValue(stdnum.normalizedValue());
137                stdnumMapper.parse(context);
138            }
139        } catch(NumberFormatException e) {
140            context.externalValue("unknown");
141            stdnumMapper.parse(context);
142        }
143    }
144
145    @Override
146    public void merge(Mapper mergeWith, MergeContext mergeContext) throws MergeMappingException {
147    }
148
149    @Override
150    public void traverse(FieldMapperListener fieldMapperListener) {
151        contentMapper.traverse(fieldMapperListener);
152        stdnumMapper.traverse(fieldMapperListener);
153    }
154
155    @Override
156    public void traverse(ObjectMapperListener objectMapperListener) {
157    }
158
159    @Override
160    public void close() {
161        contentMapper.close();
162        stdnumMapper.close();
163    }
164
165    @Override
166    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
167        builder.startObject(name);
168        builder.field("type", CONTENT_TYPE);
169
170        builder.startObject("fields");
171        contentMapper.toXContent(builder, params);
172        stdnumMapper.toXContent(builder, params);
173        builder.endObject();
174
175        builder.endObject();
176        return builder;
177    }
178}