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}