001/* 002 * Copyright (C) 2014 Jörg Prante 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.xbib.elasticsearch.plugin.jdbc.util; 017 018import org.elasticsearch.common.xcontent.ToXContent; 019import org.elasticsearch.common.xcontent.XContentBuilder; 020 021import java.io.IOException; 022import java.util.LinkedHashMap; 023import java.util.List; 024import java.util.Map; 025 026import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; 027 028/** 029 * A plain indexable object. The indexable object can store meta data and core data. 030 * The indexable object can be iterated and passed to an XContentBuilder. The individual values 031 * are formatted for JSON, which should be the correct format 032 */ 033public class PlainIndexableObject implements IndexableObject, ToXContent, Comparable<IndexableObject> { 034 035 private Map<String, String> meta; 036 037 private Map<String, Object> core; 038 039 private boolean ignoreNull; 040 041 public PlainIndexableObject() { 042 this.meta = new LinkedHashMap<String, String>(); 043 this.core = new LinkedHashMap<String, Object>(); 044 } 045 046 @Override 047 public IndexableObject ignoreNull(boolean ignorenull) { 048 this.ignoreNull = ignorenull; 049 return this; 050 } 051 052 @Override 053 public IndexableObject optype(String optype) { 054 meta.put(ControlKeys._optype.name(), optype); 055 return this; 056 } 057 058 @Override 059 public String optype() { 060 return meta.get(ControlKeys._optype.name()); 061 } 062 063 @Override 064 public IndexableObject index(String index) { 065 meta.put(ControlKeys._index.name(), index); 066 return this; 067 } 068 069 @Override 070 public String index() { 071 return meta.get(ControlKeys._index.name()); 072 } 073 074 @Override 075 public IndexableObject type(String type) { 076 meta.put(ControlKeys._type.name(), type); 077 return this; 078 } 079 080 @Override 081 public String type() { 082 return meta.get(ControlKeys._type.name()); 083 } 084 085 @Override 086 public IndexableObject id(String id) { 087 meta.put(ControlKeys._id.name(), id); 088 return this; 089 } 090 091 @Override 092 public String id() { 093 return meta.get(ControlKeys._id.name()); 094 } 095 096 @Override 097 public IndexableObject meta(String key, String value) { 098 meta.put(key, value); 099 return this; 100 } 101 102 @Override 103 public String meta(String key) { 104 return meta.get(key); 105 } 106 107 @Override 108 public IndexableObject source(Map<String, Object> source) { 109 this.core = source; 110 return this; 111 } 112 113 @Override 114 public Map<String, Object> source() { 115 return core; 116 } 117 118 /** 119 * Build a string that can be used for indexing. 120 * 121 * @throws java.io.IOException when build gave an error 122 */ 123 @Override 124 public String build() throws IOException { 125 XContentBuilder builder = jsonBuilder(); 126 toXContent(builder, ToXContent.EMPTY_PARAMS); 127 return builder.string(); 128 } 129 130 @Override 131 public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { 132 toXContent(builder, params, core); 133 return builder; 134 } 135 136 /** 137 * Recursive method to build XContent from a key/value map of Values 138 * 139 * @param builder the builder 140 * @param map the map 141 * @return the XContent builder 142 * @throws java.io.IOException when method gave an error 143 */ 144 @SuppressWarnings({"unchecked"}) 145 protected XContentBuilder toXContent(XContentBuilder builder, Params params, Map<String, Object> map) throws IOException { 146 if (checkCollapsedMapLength(map)) { 147 return builder; 148 } 149 builder.startObject(); 150 for (Map.Entry<String, Object> k : map.entrySet()) { 151 Object o = k.getValue(); 152 if (ignoreNull && (o == null || (o instanceof Values) && ((Values) o).isNull())) { 153 continue; 154 } 155 builder.field(k.getKey()); 156 if (o instanceof Values) { 157 Values v = (Values) o; 158 v.toXContent(builder, params); 159 } else if (o instanceof Map) { 160 toXContent(builder, params, (Map<String, Object>) o); 161 } else if (o instanceof List) { 162 toXContent(builder, params, (List) o); 163 } else { 164 try { 165 builder.value(o); 166 } catch (Throwable e) { 167 throw new IOException("unknown object class for value:" + o.getClass().getName() + " " + o); 168 } 169 } 170 } 171 builder.endObject(); 172 return builder; 173 } 174 175 /** 176 * Check if the map is empty, after optional null value removal. 177 * 178 * @param map the map to check 179 * @return true if map is empty, false if not 180 */ 181 protected boolean checkCollapsedMapLength(Map<String, Object> map) { 182 int exists = 0; 183 for (Map.Entry<String, Object> k : map.entrySet()) { 184 Object o = k.getValue(); 185 if (ignoreNull && (o == null || (o instanceof Values) && ((Values) o).isNull())) { 186 continue; 187 } 188 exists++; 189 } 190 return exists == 0; 191 } 192 193 @SuppressWarnings({"unchecked"}) 194 protected XContentBuilder toXContent(XContentBuilder builder, Params params, List list) throws IOException { 195 builder.startArray(); 196 for (Object o : list) { 197 if (o instanceof Values) { 198 Values v = (Values) o; 199 v.toXContent(builder, ToXContent.EMPTY_PARAMS); 200 } else if (o instanceof Map) { 201 toXContent(builder, params, (Map<String, Object>) o); 202 } else if (o instanceof List) { 203 toXContent(builder, params, (List) o); 204 } else { 205 try { 206 builder.value(o); 207 } catch (Exception e) { 208 throw new IOException("unknown object class:" + o.getClass().getName()); 209 } 210 } 211 } 212 builder.endArray(); 213 return builder; 214 } 215 216 @Override 217 public boolean isEmpty() { 218 return optype() == null && index() == null && type() == null && id() == null && core.isEmpty(); 219 } 220 221 @Override 222 public String toString() { 223 return "[" + optype() + "/" + index() + "/" + type() + "/" + id() + "]->" + core; 224 } 225 226 @Override 227 public int compareTo(IndexableObject o) { 228 if (o == null) { 229 return -1; 230 } 231 String s1 = optype() + '/' + index() + '/' + type() + '/' + id(); 232 String s2 = o.optype() + '/' + o.index() + '/' + o.type() + '/' + o.id(); 233 return s1.compareTo(s2); 234 } 235 236 @Override 237 public boolean equals(Object o) { 238 return o != null && o instanceof IndexableObject && hashCode() == o.hashCode(); 239 } 240 241 @Override 242 public int hashCode() { 243 int hash = 3; 244 hash = 37 * hash + (optype() != null ? optype().hashCode() : 0); 245 hash = 37 * hash + (index() != null ? index().hashCode() : 0); 246 hash = 37 * hash + (type() != null ? type().hashCode() : 0); 247 hash = 37 * hash + (id() != null ? id().hashCode() : 0); 248 return hash; 249 } 250 251}