/*
* 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.usergrid.persistence.model.entity;
import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import org.apache.usergrid.persistence.model.field.*;
import org.apache.usergrid.persistence.model.field.value.EntityObject;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* abstract conversion to Map<String,Object> form EntityObject
*/
public class EntityToMapConverter {
public static final String LAT = "latitude";
public static final String LON = "longitude";
private final JsonFactory jsonFactory = new JsonFactory();
private final ObjectMapper objectMapper = new ObjectMapper(jsonFactory).registerModule(new GuavaModule());
private static final Map<String,Boolean> corruptedTypes = getCorruptedTypes();
/**
* Convert Entity to Map, adding version_ug_field and a {name}_ug_analyzed field for each StringField.
*/
/**
* hacky impl, for outbound implementations longitude needs to be \ "longitude" and not "lon"
* @param entityObject
* @return
*/
public EntityMap toMap( EntityObject entityObject ) {
EntityMap map = new EntityMap();
return toMap(entityObject, map);
}
private EntityMap toMap( EntityObject entity, EntityMap entityMap ) {
for ( Field field : entity.getFields() ) {
if( field instanceof DistanceField){
//parse distance and add to metadata
if(!entityMap.containsKey("metadata"))entityMap.put("metadata",new HashMap<String,Object>());
DistanceField distanceField = (DistanceField) field;
Map<String,Object> metaMap = (Map) entityMap.get("metadata");
metaMap.put(DistanceField.NAME, distanceField.getValue());
}else if ( field instanceof ListField || field instanceof ArrayField || field instanceof SetField) {
Collection list = ( Collection ) field.getValue();
entityMap.put( field.getName(), processCollection( list ) );
}
else if ( field instanceof EntityObjectField ) {
EntityObject eo = ( EntityObject ) field.getValue();
entityMap.put( field.getName(), toMap( eo ) ); // recursion
}
else if ( field instanceof LocationField ) {
LocationField locField = ( LocationField ) field;
Map<String, Object> locMap = new HashMap<String, Object>();
// field names lat and lon trigger ElasticSearch geo location
locMap.put( LAT, locField.getValue().getLatitude() );
locMap.put( LON, locField.getValue().getLongitude() );
entityMap.put( field.getName(), locMap );
}
else if ( field instanceof ByteArrayField ) {
ByteArrayField bf = ( ByteArrayField ) field;
if( corruptedTypes.containsKey(bf.getClassinfo().getName()) ){
//do not deserialize this contains Query and Query has changed
continue;
}
byte[] serilizedObj = bf.getValue();
Object o;
try {
o = objectMapper.readValue( serilizedObj, bf.getClassinfo() );
}
catch ( IOException e ) {
throw new RuntimeException( "Can't deserialize object from field:"
+ field.getName()+ " classinfo: " + bf.getClassinfo()
+ " byteArray of length:" + serilizedObj.length
, e );
}
entityMap.put( bf.getName(), o );
}else if (field instanceof SerializedObjectField) {
SerializedObjectField bf = (SerializedObjectField) field;
String serilizedObj = bf.getValue();
Object o;
try {
o = objectMapper.readValue(serilizedObj, bf.getClassinfo());
} catch (IOException e) {
throw new RuntimeException("Can't deserialize object " + serilizedObj, e);
}
entityMap.put(bf.getName(), o);
} else {
entityMap.put(field.getName(), field.getValue());
}
}
return entityMap;
}
/**
* Process the collection for our map
* @param c
* @return
*/
private List<?> processCollection( Collection c ) {
if ( c.isEmpty() ) {
return Collections.emptyList();
}
List processed = new ArrayList(c.size());
for(final Object element: c){
processed.add( processCollectionElement( element ) );
}
return processed;
}
/**
* Process each instance of data in our collection
* @param element
* @return
*/
private Object processCollectionElement( final Object element ) {
if ( element instanceof EntityObject ) {
return toMap((EntityObject) element);
}
//recurse into another list structure (2d + arrays)
if (element instanceof ListField || element instanceof ArrayField || element instanceof SetField){
return processCollection( ( Collection ) ( ( AbstractField ) element ).getValue() );
}
if ( element instanceof List || element instanceof Set ) {
return processCollection( ( Collection ) element ); // recursion;
}
return element;
}
public static Map<String,Boolean> getCorruptedTypes() {
Map<String,Boolean> typeMap = new HashMap<>() ;
typeMap.put("org.apache.usergrid.persistence.PathQuery",true);
return typeMap;
}
}