/**
* OpenSpotLight - Open Source IT Governance Platform
*
* Copyright (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA
* or third-party contributors as indicated by the @author tags or express
* copyright attribution statements applied by the authors. All third-party
* contributions are distributed under license by CARAVELATECH CONSULTORIA E
* TECNOLOGIA EM INFORMATICA LTDA.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
***********************************************************************
* OpenSpotLight - Plataforma de Governança de TI de Código Aberto
*
* Direitos Autorais Reservados (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA
* EM INFORMATICA LTDA ou como contribuidores terceiros indicados pela etiqueta
* @author ou por expressa atribuição de direito autoral declarada e atribuída pelo autor.
* Todas as contribuições de terceiros estão distribuídas sob licença da
* CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA.
*
* Este programa é software livre; você pode redistribuí-lo e/ou modificá-lo sob os
* termos da Licença Pública Geral Menor do GNU conforme publicada pela Free Software
* Foundation.
*
* Este programa é distribuído na expectativa de que seja útil, porém, SEM NENHUMA
* GARANTIA; nem mesmo a garantia implícita de COMERCIABILIDADE OU ADEQUAÇÃO A UMA
* FINALIDADE ESPECÍFICA. Consulte a Licença Pública Geral Menor do GNU para mais detalhes.
*
* Você deve ter recebido uma cópia da Licença Pública Geral Menor do GNU junto com este
* programa; se não, escreva para:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.openspotlight.persist.support;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newLinkedList;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;
import static java.lang.Class.forName;
import static java.text.MessageFormat.format;
import static java.util.Collections.reverse;
import static java.util.Collections.sort;
import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptors;
import static org.openspotlight.common.util.Assertions.checkCondition;
import static org.openspotlight.common.util.Assertions.checkNotNull;
import static org.openspotlight.common.util.Exceptions.logAndReturnNew;
import static org.openspotlight.common.util.Reflection.unwrapCollectionFromMethodReturn;
import static org.openspotlight.common.util.Reflection.unwrapMapFromMethodReturn;
import static org.openspotlight.common.util.SLCollections.iterableToList;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.SerializationUtils;
import org.openspotlight.common.Pair;
import org.openspotlight.common.collection.IteratorBuilder;
import org.openspotlight.common.exception.SLRuntimeException;
import org.openspotlight.common.util.Conversion;
import org.openspotlight.common.util.Reflection;
import org.openspotlight.common.util.Wrapper;
import org.openspotlight.persist.annotation.IndexedProperty;
import org.openspotlight.persist.annotation.KeyProperty;
import org.openspotlight.persist.annotation.Name;
import org.openspotlight.persist.annotation.ParentProperty;
import org.openspotlight.persist.annotation.PersistPropertyAsStream;
import org.openspotlight.persist.annotation.SimpleNodeType;
import org.openspotlight.persist.annotation.TransientProperty;
import org.openspotlight.persist.internal.LazyProperty;
import org.openspotlight.persist.internal.StreamPropertyWithParent;
import org.openspotlight.storage.NodeCriteria.NodeCriteriaBuilder;
import org.openspotlight.storage.Partition;
import org.openspotlight.storage.StorageSession;
import org.openspotlight.storage.domain.NodeFactory;
import org.openspotlight.storage.domain.StorageNode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.inject.Singleton;
/**
* Created by IntelliJ IDEA. User: feuteston Date: 05/04/2010 Time: 13:19:32 To change this template use File | Settings | File
* Templates.
*/
@Singleton
public class SimplePersistImpl implements
SimplePersistCapable<StorageNode, StorageSession> {
private static class BeanToNodeChildData {
final Collection<SimpleNodeType> childrenToSave;
final boolean multiple;
final Class<?> nodeType;
final String propertyName;
private BeanToNodeChildData(final String propertyName,
final boolean multiple, final Class<?> nodeType) {
this.propertyName = propertyName;
this.nodeType = nodeType;
childrenToSave = newHashSet();
this.multiple = multiple;
}
}
private static class ConversionToBeanContext {
final Wrapper<SimpleNodeType> beanReference = Wrapper.createMutable();
final Map<StorageNode, Object> beansConverted = newHashMap();
final StorageNode node;
private ConversionToBeanContext(final StorageNode node) {
this.node = node;
}
}
private static class ConversionToNodeContext {
final Set<StorageNode> allNodes = newHashSet();
final SimpleNodeType bean;
final Wrapper<StorageNode> nodeReference = Wrapper.createMutable();
final Map<Object, StorageNode> nodesConverted = newHashMap();
ConversionToNodeContext(final SimpleNodeType bean) {
this.bean = bean;
}
}
private static class Descriptors {
private List<Pair<String, SimpleNodeType>> rootObjects = null;
final SimpleNodeType bean;
final Class<?> beanType;
final List<PropertyDescriptor> childrenPropertiesDescriptor;
final List<PropertyDescriptor> keyPropertiesDescriptor;
final List<PropertyDescriptor> lazyPropertiesDescriptor;
final List<PropertyDescriptor> parentPropertiesDescriptor;
final Wrapper<PropertyDescriptor> parentPropertyDescriptor;
final List<PropertyDescriptor> simplePropertiesDescriptor;
final List<PropertyDescriptor> streamPropertiesDescriptor;
private Descriptors(final Class<?> beanType, final SimpleNodeType bean,
final Wrapper<PropertyDescriptor> parentPropertyDescriptor,
final List<PropertyDescriptor> simplePropertiesDescriptor,
final List<PropertyDescriptor> keyPropertiesDescriptor,
final List<PropertyDescriptor> streamPropertiesDescriptor,
final List<PropertyDescriptor> childrenPropertiesDescriptor,
final List<PropertyDescriptor> parentPropertiesDescriptor,
final List<PropertyDescriptor> lazyPropertiesDescriptor) {
this.beanType = beanType;
this.bean = bean;
this.parentPropertyDescriptor = parentPropertyDescriptor;
this.simplePropertiesDescriptor = simplePropertiesDescriptor;
this.keyPropertiesDescriptor = keyPropertiesDescriptor;
this.streamPropertiesDescriptor = streamPropertiesDescriptor;
this.childrenPropertiesDescriptor = childrenPropertiesDescriptor;
this.parentPropertiesDescriptor = parentPropertiesDescriptor;
this.lazyPropertiesDescriptor = lazyPropertiesDescriptor;
}
static <T extends SimpleNodeType> Descriptors fillDescriptors(
final Class<?> beanType)
throws Exception {
return fillDescriptors(null, beanType);
}
static <T extends SimpleNodeType> Descriptors fillDescriptors(
final Object bean)
throws Exception {
return fillDescriptors(bean, bean.getClass());
}
static <T extends SimpleNodeType> Descriptors fillDescriptors(
final Object bean, final Class<?> beanType)
throws Exception {
final Descriptors descriptors = Descriptors.createMutable(beanType,
bean);
for (final PropertyDescriptor descriptor: getPropertyDescriptors(beanType)) {
if (descriptor.getName().equals("class")) {
continue;// Object#getClass
}
final Method readMethod = descriptor.getReadMethod();
if (readMethod.isAnnotationPresent(TransientProperty.class)) {
continue;
}
final Class<?> returnType = readMethod.getReturnType();
if (readMethod.isAnnotationPresent(ParentProperty.class)) {
final Object value = bean != null ? readMethod.invoke(bean)
: null;
if (value != null
&& descriptors.parentPropertyDescriptor != null
&& descriptors.parentPropertyDescriptor
.getWrapped() != null) { throw new IllegalStateException(
"only one parent property is allowed"); }
if (value != null) {
descriptors.parentPropertyDescriptor
.setWrapped(descriptor);
}
descriptors.parentPropertiesDescriptor.add(descriptor);
} else if (readMethod.isAnnotationPresent(KeyProperty.class)) {
descriptors.keyPropertiesDescriptor.add(descriptor);
} else if (readMethod
.isAnnotationPresent(PersistPropertyAsStream.class)) {
descriptors.streamPropertiesDescriptor.add(descriptor);
} else if (returnType.isAssignableFrom(InputStream.class)) {
descriptors.streamPropertiesDescriptor.add(descriptor);
} else if (SimpleNodeType.class.isAssignableFrom(returnType)) {
descriptors.childrenPropertiesDescriptor.add(descriptor);
} else if (Collection.class.isAssignableFrom(returnType)) {
final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodInformation =
unwrapCollectionFromMethodReturn(readMethod);
if (SimpleNodeType.class.isAssignableFrom(methodInformation
.getItemType())) {
descriptors.childrenPropertiesDescriptor
.add(descriptor);
} else {
descriptors.streamPropertiesDescriptor.add(descriptor);
}
} else if (Map.class.isAssignableFrom(returnType)) {
final Reflection.UnwrappedMapTypeFromMethodReturn<Object, Object> methodInformation =
unwrapMapFromMethodReturn(readMethod);
if (SimpleNodeType.class.isAssignableFrom(methodInformation
.getItemType().getK2())) {
descriptors.childrenPropertiesDescriptor
.add(descriptor);
} else {
descriptors.streamPropertiesDescriptor.add(descriptor);
}
} else if (LazyProperty.class.isAssignableFrom(returnType)) {
descriptors.lazyPropertiesDescriptor.add(descriptor);
} else {
descriptors.simplePropertiesDescriptor.add(descriptor);
}
}
return Descriptors.createImmutableFrom(descriptors);
}
public static Descriptors createImmutableFrom(final Descriptors from) {
return new Descriptors(
from.beanType,
from.bean,
Wrapper.<PropertyDescriptor>createImmutable(from.parentPropertyDescriptor
.getWrapped()),
ImmutableList.copyOf(from.simplePropertiesDescriptor),
ImmutableList.copyOf(from.keyPropertiesDescriptor),
ImmutableList.copyOf(from.streamPropertiesDescriptor),
ImmutableList.copyOf(from.childrenPropertiesDescriptor),
ImmutableList.copyOf(from.parentPropertiesDescriptor),
ImmutableList.copyOf(from.lazyPropertiesDescriptor));
}
public static Descriptors createMutable(final Class<?> beanType,
final Object bean) {
return new Descriptors(beanType, (SimpleNodeType) bean,
Wrapper.<PropertyDescriptor>createMutable(),
Lists.<PropertyDescriptor>newLinkedList(),
Lists.<PropertyDescriptor>newLinkedList(),
Lists.<PropertyDescriptor>newLinkedList(),
Lists.<PropertyDescriptor>newLinkedList(),
Lists.<PropertyDescriptor>newLinkedList(),
Lists.<PropertyDescriptor>newLinkedList());
}
private List<Pair<String, SimpleNodeType>> loadRootObjects()
throws Exception {
final List<Pair<String, SimpleNodeType>> resultInReverseOrder = newLinkedList();
Descriptors currentDescriptors = this;
Object parent = bean;
Object oldParent = null;
do {
final PropertyDescriptor descriptor = currentDescriptors.parentPropertyDescriptor
.getWrapped();
oldParent = parent;
final Class<?> oldType = oldParent != null ? oldParent
.getClass() : null;
parent = descriptor != null ? descriptor.getReadMethod()
.invoke(parent) : null;
currentDescriptors = parent != null ? Descriptors
.fillDescriptors(parent) : null;
String oldParentName = null;
if (oldParent != null && currentDescriptors != null) {
lookingForNames: for (final PropertyDescriptor childDescriptor: currentDescriptors.childrenPropertiesDescriptor) {
if (childDescriptor.getPropertyType().equals(oldType)) {
oldParentName = childDescriptor.getName();
break lookingForNames;
}
}
}
resultInReverseOrder.add(Pair.newPair(oldParentName,
(SimpleNodeType) oldParent));
} while (currentDescriptors != null && oldParent != null);
reverse(resultInReverseOrder);
return ImmutableList.copyOf(resultInReverseOrder);
}
public List<Pair<String, SimpleNodeType>> getRootObjects()
throws Exception {
if (rootObjects == null) {
rootObjects = loadRootObjects();
}
return rootObjects;
}
}
private class InternalMethodsImpl implements InternalMethods {
@Override
public Object beforeUnConvert(final SimpleNodeType bean,
final Serializable value, final Method readMethod) {
try {
if (value instanceof Collection) {
boolean mayBeStreamPropertyWithParent = true;
if (readMethod != null) {
final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodDescription =
unwrapCollectionFromMethodReturn(readMethod);
mayBeStreamPropertyWithParent = StreamPropertyWithParent.class
.isAssignableFrom(methodDescription
.getItemType());
}
if (mayBeStreamPropertyWithParent) {
final Collection<?> valueAsCollection = (Collection<?>) value;
for (final Object o: valueAsCollection) {
if (o instanceof StreamPropertyWithParent) {
((StreamPropertyWithParent) o).setParent(bean);
}
}
}
} else if (value instanceof Map) {
boolean mayBeStreamPropertyWithParent = true;
if (readMethod != null) {
final Reflection.UnwrappedMapTypeFromMethodReturn<Object, Object> methodDescription =
unwrapMapFromMethodReturn(readMethod);
mayBeStreamPropertyWithParent = StreamPropertyWithParent.class
.isAssignableFrom(methodDescription
.getItemType().getK2());
}
if (mayBeStreamPropertyWithParent) {
final Map<?, ?> valueAsMap = (Map<?, ?>) value;
for (final Map.Entry<?, ?> entry: valueAsMap
.entrySet()) {
if (entry.getValue() instanceof StreamPropertyWithParent) {
((StreamPropertyWithParent) entry.getValue())
.setParent(bean);
}
}
}
} else if (value instanceof StreamPropertyWithParent) {
((StreamPropertyWithParent) value).setParent(bean);
}
return value;
} catch (final Exception e) {
throw logAndReturnNew(e, SLRuntimeException.class);
}
}
@Override
public String getNodeName(final Class<?> nodeType) {
return internalGetNodeName(nodeType);
}
}
private static final String[] EMPTY_NAMES = new String[] {};
private static final Object[] EMPTY_VALUES = new Object[] {};
private static final String NODE_ENTRY_TYPE = "internal-node-entry-type";
private static final String NODE_PROPERTY_NAME = "internal-node-proeprty-name";
private static final String SHA1_PROPERTY_NAME = "internal-{0}-sha1";
private final Partition currentPartition;
private final StorageSession currentSession;
private final InternalMethods internalMethods = new InternalMethodsImpl();
public SimplePersistImpl(final StorageSession currentSession,
final Partition currentPartition) {
this.currentSession = currentSession;
this.currentPartition = currentPartition;
}
private static <T extends Serializable> T asObject(final InputStream is)
throws Exception {
if (is == null) { return null; }
final ObjectInputStream ois = new ObjectInputStream(is);
final T result = (T) ois.readObject();
return result;
}
private static <T extends Serializable> InputStream asStream(final T o)
throws Exception {
if (o == null) { return null; }
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ObjectOutputStream ois = new ObjectOutputStream(baos);
ois.writeObject(o);
ois.flush();
return new ByteArrayInputStream(baos.toByteArray());
}
private Serializable beforeSerializeList(
final List<? extends Serializable> value, final Method readMethod)
throws Exception {
if (readMethod != null) {
final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodDescription =
unwrapCollectionFromMethodReturn(readMethod);
if (StreamPropertyWithParent.class
.isAssignableFrom(methodDescription.getItemType())) {
final LinkedList<Serializable> newCollection = newLinkedList();
for (final Serializable o: value) {
newCollection.add(beforeSerializeSerializable(o));
}
return newCollection;
}
return (Serializable) value;
} else {
final LinkedList<Serializable> newCollection = newLinkedList();
for (final Serializable o: value) {
newCollection.add(beforeSerializeSerializable(o));
}
return newCollection;
}
}
private Serializable beforeSerializeMap(
final Map<? extends Serializable, ? extends Serializable> value,
final Method readMethod)
throws Exception {
if (readMethod != null) {
final Reflection.UnwrappedMapTypeFromMethodReturn<Object, Object> methodDescription =
unwrapMapFromMethodReturn(readMethod);
if (StreamPropertyWithParent.class
.isAssignableFrom(methodDescription.getItemType().getK2())) {
final HashMap<Serializable, Serializable> newMap = newHashMap();
for (final Map.Entry<? extends Serializable, ? extends Serializable> entry: value
.entrySet()) {
newMap.put(entry.getKey(),
beforeSerializeSerializable(entry.getValue()));
}
return newMap;
}
return (Serializable) value;
} else {
final HashMap<Serializable, Serializable> newMap = newHashMap();
for (final Map.Entry<? extends Serializable, ? extends Serializable> entry: value
.entrySet()) {
newMap.put(entry.getKey(),
beforeSerializeSerializable(entry.getValue()));
}
return newMap;
}
}
private Serializable beforeSerializeSerializable(final Serializable value) {
if (value instanceof StreamPropertyWithParent) {
final StreamPropertyWithParent typedValue = (StreamPropertyWithParent) value;
final SimpleNodeType oldParent = typedValue.getParent();
typedValue.setParent(null);
final StreamPropertyWithParent newValue = (StreamPropertyWithParent) SerializationUtils
.clone(value);
typedValue.setParent(oldParent);
return newValue;
}
return value;
}
private Serializable beforeSerializeSet(
final Set<? extends Serializable> value, final Method readMethod)
throws Exception {
if (readMethod != null) {
final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodDescription =
unwrapCollectionFromMethodReturn(readMethod);
if (StreamPropertyWithParent.class
.isAssignableFrom(methodDescription.getItemType())) {
final HashSet<Serializable> newCollection = newHashSet();
for (final Serializable o: value) {
newCollection.add(beforeSerializeSerializable(o));
}
return newCollection;
}
return (Serializable) value;
} else {
final HashSet<Serializable> newCollection = newHashSet();
for (final Serializable o: value) {
newCollection.add(beforeSerializeSerializable(o));
}
return newCollection;
}
}
private Map<String, PropertyDescriptor> createMapWith(
final PropertyDescriptor[] propertyDescriptors) {
final ImmutableMap.Builder<String, PropertyDescriptor> builder = ImmutableMap
.<String, PropertyDescriptor>builder();
for (final PropertyDescriptor d: propertyDescriptors) {
builder.put(d.getName(), d);
}
return builder.build();
}
private <T> StorageNode createNewNode(
final ConversionToNodeContext context,
final StorageNode parentNode, final Descriptors descriptors,
final String propertyName)
throws Exception {
final String name = internalGetNodeName(descriptors.bean);
final NodeFactory.NodeBuilder builder = currentSession.withPartition(
currentPartition).createNodeWithType(name);
if (parentNode != null) {
builder.withParent(parentNode);
}
for (final PropertyDescriptor descriptor: descriptors.keyPropertiesDescriptor) {
builder.withSimpleKey(descriptor.getName(), Conversion.convert(
descriptor.getReadMethod().invoke(descriptors.bean),
String.class));
}
if (propertyName != null) {
builder.withSimpleKey(NODE_PROPERTY_NAME, propertyName);
}
final StorageNode newNode = builder.andCreate();
newNode.setIndexedProperty(currentSession, NODE_ENTRY_TYPE,
internalGetNodeType(descriptors.bean));
return newNode;
}
private <T> void fillBeanChildren(final ConversionToBeanContext context,
final StorageNode node, final T bean,
final List<PropertyDescriptor> childrenPropertiesDescriptor)
throws Exception {
for (final PropertyDescriptor descriptor: childrenPropertiesDescriptor) {
final Class<?> propertyType = descriptor.getPropertyType();
Class<?> nodeType = null;
final boolean isMultiple = Collection.class
.isAssignableFrom(propertyType);
final Method readMethod = descriptor.getReadMethod();
if (isMultiple) {
final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodDescription =
unwrapCollectionFromMethodReturn(readMethod);
if (List.class.isAssignableFrom(propertyType)) {
descriptor.getWriteMethod().invoke(bean, newLinkedList());
} else if (Set.class.isAssignableFrom(propertyType)) {
descriptor.getWriteMethod().invoke(bean, newHashSet());
} else {
throw new IllegalStateException("wrong child type");
}
nodeType = methodDescription.getItemType();
} else if (SimpleNodeType.class.isAssignableFrom(propertyType)) {
nodeType = propertyType;
} else {
throw new IllegalStateException("wrong child type");
}
if (!SimpleNodeType.class.isAssignableFrom(nodeType)) { throw new IllegalStateException("wrong child type"); }
final String childrenName = internalGetNodeName(nodeType);
Iterable<StorageNode> children = iterableToList(node
.getChildren(currentPartition, currentSession,
childrenName));
children = filterChildrenWithProperty(children,
descriptor.getName());
final List<Object> childrenAsBeans = newLinkedList();
for (final StorageNode child: children) {
childrenAsBeans.add(internalConvertNodeToBean(context, child,
bean));
}
if (isMultiple) {
final Collection c = (Collection) readMethod.invoke(bean);
for (final Object o: childrenAsBeans) {
c.add(o);
}
if (Comparable.class.isAssignableFrom(nodeType)
&& c instanceof List) {
sort((List) c);
}
} else if (childrenAsBeans.size() > 0) {
final Object value = childrenAsBeans.iterator().next();
descriptor.getWriteMethod().invoke(bean, value);
}
}
}
private void fillBeanLazyProperties(final StorageNode cached,
final Object bean,
final List<PropertyDescriptor> lazyPropertiesDescriptors)
throws Exception {
for (final PropertyDescriptor property: lazyPropertiesDescriptors) {
final String propertyName = property.getName();
final LazyProperty<?> lazyProperty = (LazyProperty<?>) property
.getReadMethod().invoke(bean);
lazyProperty.getMetadata().setParentKey(cached.getKey());
lazyProperty.getMetadata().setSavedNode(cached);
lazyProperty.getMetadata().setPropertyName(propertyName);
final String sha1 = cached.getPropertyValueAsString(currentSession,
format(SHA1_PROPERTY_NAME, propertyName));
lazyProperty.getMetadata().internalSetSha1(sha1);
}
}
private void fillBeanParent(
final List<PropertyDescriptor> parentPropertyDescriptors,
final Object bean, final Object beanParent)
throws Exception {
if (beanParent != null) {
final Class<?> parentType = beanParent.getClass();
for (final PropertyDescriptor descriptor: parentPropertyDescriptors) {
if (descriptor.getPropertyType().isAssignableFrom(parentType)) {
descriptor.getWriteMethod().invoke(bean, beanParent);
break;
}
}
}
}
private <T> void fillBeanSimpleProperties(final StorageNode node,
final T bean,
final List<PropertyDescriptor> simplePropertiesDescriptor)
throws Exception {
for (final PropertyDescriptor descriptor: simplePropertiesDescriptor) {
final String value = node.getPropertyValueAsString(currentSession,
descriptor.getName());
if (value == null && descriptor.getPropertyType().isPrimitive()) {
continue;
}
descriptor.getWriteMethod().invoke(bean,
Conversion.convert(value, descriptor.getPropertyType()));
}
}
private <T> void fillBeanStreamProperties(final StorageNode node,
final T bean,
final List<PropertyDescriptor> streamPropertiesDescriptor)
throws Exception {
for (final PropertyDescriptor descriptor: streamPropertiesDescriptor) {
final Class<?> propertyType = descriptor.getPropertyType();
if (InputStream.class.isAssignableFrom(propertyType)) {
final InputStream value = node.getPropertyValueAsStream(
currentSession, descriptor.getName());
descriptor.getWriteMethod().invoke(bean, value);
} else if (Serializable.class.isAssignableFrom(propertyType)
|| Collection.class.isAssignableFrom(propertyType)
|| Map.class.isAssignableFrom(propertyType)) {
final Serializable value = asObject(node.getPropertyValueAsStream(
currentSession, descriptor.getName()));
descriptor.getWriteMethod().invoke(
bean,
getInternalMethods().beforeUnConvert(
(SimpleNodeType) bean, value,
descriptor.getReadMethod()));
} else {
throw new IllegalStateException("wrong type");
}
}
}
private <T> void fillNodeChildrenProperties(
final ConversionToNodeContext context, final T bean,
final List<PropertyDescriptor> childrenPropertiesDescriptor,
final StorageNode newNodeEntry)
throws Exception {
final Map<String, BeanToNodeChildData> nodesToConvert = newHashMap();
for (final PropertyDescriptor property: childrenPropertiesDescriptor) {
final String propertyName = property.getName();
BeanToNodeChildData data = nodesToConvert.get(propertyName);
final Class<?> propertyType = property.getPropertyType();
final Object value = property.getReadMethod().invoke(bean);
if (SimpleNodeType.class.isAssignableFrom(propertyType)) {
if (data == null) {
data = new BeanToNodeChildData(propertyName,
Collection.class.isAssignableFrom(propertyType),
propertyType);
nodesToConvert.put(propertyName, data);
}
data.childrenToSave.add((SimpleNodeType) value);
} else if (Collection.class.isAssignableFrom(propertyType)) {
final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodInformation =
unwrapCollectionFromMethodReturn(property
.getReadMethod());
if (data == null) {
data = new BeanToNodeChildData(propertyName,
Collection.class.isAssignableFrom(propertyType),
methodInformation.getItemType());
nodesToConvert.put(propertyName, data);
}
if (List.class.isAssignableFrom(methodInformation
.getCollectionType())) {
for (final SimpleNodeType t: (List<SimpleNodeType>) value) {
data.childrenToSave.add(t);
}
} else if (Set.class.isAssignableFrom(methodInformation
.getCollectionType())) {
for (final SimpleNodeType t: (Set<SimpleNodeType>) value) {
data.childrenToSave.add(t);
}
} else {
throw new IllegalStateException("invalid collection type");
}
} else {
throw new IllegalStateException("invalid type:"
+ property.getPropertyType());
}
}
for (final BeanToNodeChildData data: nodesToConvert.values()) {
if (!data.multiple && data.childrenToSave.size() > 1) { throw new IllegalStateException(
"single property with more than one child"); }
for (final SimpleNodeType beanBeenSaved: data.childrenToSave) {
internalConvertBeanToNode(context, data.propertyName,
beanBeenSaved, newNodeEntry);
}
context.allNodes.addAll(iterableToList(newNodeEntry
.getChildren(currentPartition, currentSession,
internalGetNodeName(data.nodeType))));
}
}
private void fillNodeLazyProperties(final ConversionToNodeContext context,
final SimpleNodeType bean,
final List<PropertyDescriptor> lazyPropertiesDescriptor,
final StorageNode nodeEntry)
throws Exception {
for (final PropertyDescriptor descriptor: lazyPropertiesDescriptor) {
final LazyProperty<?> property = (LazyProperty<?>) descriptor
.getReadMethod().invoke(bean);
if (property != null && property.getMetadata().needsSave()) {
final String propertyName = descriptor.getName();
final Object value = property.getMetadata().getTransient();
if (value instanceof InputStream) {
nodeEntry.setSimpleProperty(currentSession, propertyName,
(InputStream) value);
} else if (value instanceof Set) {
nodeEntry
.setSimpleProperty(
currentSession,
propertyName,
asStream(beforeSerializeSet(
(Set<? extends Serializable>) value,
null)));
} else if (value instanceof List) {
nodeEntry
.setSimpleProperty(
currentSession,
propertyName,
asStream(beforeSerializeList(
(List<? extends Serializable>) value,
null)));
} else if (value instanceof Map) {
nodeEntry
.setSimpleProperty(
currentSession,
propertyName,
asStream(beforeSerializeMap(
(Map<? extends Serializable, ? extends Serializable>) value,
null)));
} else {// Serializable
nodeEntry
.setSimpleProperty(
currentSession,
propertyName,
asStream(beforeSerializeSerializable((Serializable) value)));
}
nodeEntry.setSimpleProperty(currentSession,
format(SHA1_PROPERTY_NAME, propertyName), property
.getMetadata().getSha1());
property.getMetadata().markAsSaved();
}
;
}
}
private <T> void fillNodeSimpleProperties(final T bean,
final List<PropertyDescriptor> simplePropertiesDescriptor,
final StorageNode newNodeEntry)
throws Exception {
for (final PropertyDescriptor property: simplePropertiesDescriptor) {
if (property.getReadMethod().isAnnotationPresent(
IndexedProperty.class)) {
newNodeEntry.setIndexedProperty(currentSession, property
.getName(), Conversion.convert(property.getReadMethod()
.invoke(bean), String.class));
} else {
newNodeEntry.setSimpleProperty(currentSession, property
.getName(), Conversion.convert(property.getReadMethod()
.invoke(bean), String.class));
}
}
}
private <T> void fillNodeStreamProperties(final T bean,
final List<PropertyDescriptor> streamPropertiesDescriptor,
final StorageNode newNodeEntry)
throws Exception {
for (final PropertyDescriptor property: streamPropertiesDescriptor) {
final Class<?> propertyType = property.getPropertyType();
final Method readMethod = property.getReadMethod();
final Object value = readMethod.invoke(bean);
if (InputStream.class.isAssignableFrom(propertyType)) {
newNodeEntry.setSimpleProperty(currentSession,
property.getName(), (InputStream) value);
} else if (Collection.class.isAssignableFrom(propertyType)) {
final Reflection.UnwrappedCollectionTypeFromMethodReturn<Object> methodInformation =
unwrapCollectionFromMethodReturn(property
.getReadMethod());
if (List.class.isAssignableFrom(methodInformation
.getCollectionType())) {
newNodeEntry.setSimpleProperty(
currentSession,
property.getName(),
asStream(beforeSerializeList(
(List<? extends Serializable>) value,
readMethod)));
} else if (Set.class.isAssignableFrom(methodInformation
.getCollectionType())) {
newNodeEntry.setSimpleProperty(
currentSession,
property.getName(),
asStream(beforeSerializeSet(
(Set<? extends Serializable>) value,
readMethod)));
} else {
throw new IllegalStateException("invalid collection type");
}
} else if (Map.class.isAssignableFrom(propertyType)) {
newNodeEntry
.setSimpleProperty(
currentSession,
property.getName(),
asStream(beforeSerializeMap(
(Map<? extends Serializable, ? extends Serializable>) value,
readMethod)));
} else if (Serializable.class.isAssignableFrom(propertyType)) {
if (propertyType.equals(String.class)
|| Number.class.isAssignableFrom(propertyType)
|| propertyType.isPrimitive()
|| Boolean.class.equals(propertyType)
|| Character.class.equals(propertyType)
|| Date.class.equals(propertyType)) {
newNodeEntry.setSimpleProperty(currentSession,
property.getName(),
Conversion.convert(value, String.class));
} else {
newNodeEntry
.setSimpleProperty(
currentSession,
property.getName(),
asStream(beforeSerializeSerializable((Serializable) value)));
}
} else {
throw new IllegalStateException("invalid type");
}
}
}
private Iterable<StorageNode> filterChildrenWithProperty(
final Iterable<StorageNode> children, final String name) {
if (name == null) { return children; }
final List<StorageNode> filtered = newLinkedList();
for (final StorageNode e: children) {
final String propertyValue = e.getPropertyValueAsString(currentSession,
NODE_PROPERTY_NAME);
if (name.equals(propertyValue)) {
filtered.add(e);
}
}
return filtered;
}
private Class<?> findClassFromNode(final StorageNode nodeEntry)
throws Exception {
return forName(nodeEntry.getPropertyValueAsString(currentSession,
NODE_ENTRY_TYPE));
}
private <T> StorageNode internalConvertBeanToNode(
final ConversionToNodeContext context, final String propertyName,
final SimpleNodeType bean, final StorageNode parentNode)
throws Exception {
final boolean firstInvocation = bean == null;
if (firstInvocation) {
StorageNode currentParentNode = parentNode;
final Descriptors currentBeanDescriptors = Descriptors
.fillDescriptors(context.bean);
final List<Pair<String, SimpleNodeType>> rootObjects = currentBeanDescriptors
.getRootObjects();
for (final Pair<String, SimpleNodeType> pair: rootObjects) {
currentParentNode = internalConvertBeanToNode(context,
pair.getK1(), pair.getK2(), currentParentNode);
}
context.allNodes.removeAll(context.nodesConverted.values());
for (final StorageNode unusedNode: context.allNodes) {
currentSession.removeNode(unusedNode);
}
return context.nodeReference.getWrapped();
} else {
StorageNode cached = context.nodesConverted.get(bean);
if (cached == null) {
final Descriptors parentDescriptors = Descriptors
.fillDescriptors(bean);
cached = createNewNode(context, parentNode, parentDescriptors,
propertyName);
context.nodesConverted.put(bean, cached);
fillNodeSimpleProperties(bean,
parentDescriptors.simplePropertiesDescriptor, cached);
fillNodeStreamProperties(bean,
parentDescriptors.streamPropertiesDescriptor, cached);
fillNodeChildrenProperties(context, bean,
parentDescriptors.childrenPropertiesDescriptor, cached);
fillNodeLazyProperties(context, bean,
parentDescriptors.lazyPropertiesDescriptor, cached);
if (bean == context.bean) {
context.nodeReference.setWrapped(cached);
}
}
return cached;
}
}
private <T> StorageNode internalConvertBeanToNode(final StorageNode parent,
final T bean)
throws Exception {
final ConversionToNodeContext context = new ConversionToNodeContext(
(SimpleNodeType) bean);
internalConvertBeanToNode(context, null, null, parent);
final StorageNode result = context.nodeReference.getWrapped();
checkNotNull("result", result);
return result;
}
private <T> Iterable<T> internalConvertNodesToBeans(
final Iterable<StorageNode> nodes)
throws Exception {
final IteratorBuilder.SimpleIteratorBuilder<T, StorageNode> b = IteratorBuilder
.createIteratorBuilder();
b.withConverter(new IteratorBuilder.Converter<T, StorageNode>() {
@Override
public T convert(final StorageNode nodeEntry)
throws Exception {
return (T) convertNodeToBean(nodeEntry);
}
});
final Iterable<T> result = b.withItems(nodes).andBuild();
return result;
}
private Object internalConvertNodeToBean(
final ConversionToBeanContext context, final StorageNode nodeEntry,
final Object beanParent)
throws Exception {
if (nodeEntry == null) {
final List<StorageNode> parents = newLinkedList();
StorageNode currentParent = context.node;
while (currentParent != null && isSimpleNode(currentParent)) {
parents.add(currentParent);
currentParent = currentParent.getParent(currentSession);
}
reverse(parents);
Object currentParentAsBean = null;
for (final StorageNode parentEntry: parents) {
currentParentAsBean = internalConvertNodeToBean(context,
parentEntry, currentParentAsBean);
}
return context.beanReference.getWrapped();
} else {
Object cached = context.beansConverted.get(nodeEntry);
if (cached == null) {
final Class<?> beanType = findClassFromNode(nodeEntry);
cached = beanType.newInstance();
context.beansConverted.put(nodeEntry, cached);
final Descriptors descriptors = Descriptors
.fillDescriptors(beanType);
fillBeanParent(descriptors.parentPropertiesDescriptor, cached,
beanParent);
fillBeanSimpleProperties(nodeEntry, cached,
descriptors.keyPropertiesDescriptor);
fillBeanSimpleProperties(nodeEntry, cached,
descriptors.simplePropertiesDescriptor);
fillBeanStreamProperties(nodeEntry, cached,
descriptors.streamPropertiesDescriptor);
fillBeanLazyProperties(nodeEntry, cached,
descriptors.lazyPropertiesDescriptor);
fillBeanChildren(context, nodeEntry, cached,
descriptors.childrenPropertiesDescriptor);
if (nodeEntry.equals(context.node)) {
context.beanReference.setWrapped((SimpleNodeType) cached);
}
}
return cached;
}
}
private <T> String internalGetNodeName(final Class<T> beanType) {
final Name annotation = beanType.getAnnotation(Name.class);
return annotation != null ? annotation.value() : beanType.getName();
}
private <T> String internalGetNodeName(final T bean) {
return this.<T>internalGetNodeName((Class<T>) bean.getClass());
}
private <T> String internalGetNodeType(final Class<T> beanType) {
return beanType.getName();
}
private String internalGetNodeType(final StorageNode node) {
return node.getType();
}
private <T> String internalGetNodeType(final T bean) {
return this.<T>internalGetNodeType((Class<T>) bean.getClass());
}
private boolean isSimpleNode(final StorageNode currentParent) {
return currentParent.getPropertyValueAsString(currentSession,
NODE_ENTRY_TYPE) != null;
}
@Override
public void closeResources() {
currentSession.closeResources();
}
@Override
public <T> Iterable<StorageNode> convertBeansToNodes(final Iterable<T> beans) {
return convertBeansToNodes(null, beans);
}
@Override
public <T> Iterable<StorageNode> convertBeansToNodes(
final StorageNode parent, final Iterable<T> beans) {
try {
final List<StorageNode> itemsConverted = newArrayList();
for (final T bean: beans) {
itemsConverted.add(convertBeanToNode(parent, bean));
}
return itemsConverted;
} catch (final Exception e) {
throw logAndReturnNew(e, SLRuntimeException.class);
}
}
@Override
public <T> StorageNode convertBeanToNode(final StorageNode parent,
final T bean) {
try {
return internalConvertBeanToNode(parent, bean);
} catch (final Exception e) {
throw logAndReturnNew(e, SLRuntimeException.class);
}
}
@Override
public <T> StorageNode convertBeanToNode(final T bean)
throws Exception {
return convertBeanToNode(null, bean);
}
@Override
public <T> Iterable<T> convertNodesToBeans(final Iterable<StorageNode> nodes) {
try {
return internalConvertNodesToBeans(nodes);
} catch (final Exception e) {
throw logAndReturnNew(e, SLRuntimeException.class);
}
}
@Override
public <T> T convertNodeToBean(final StorageNode node)
throws Exception {
final T bean = (T) internalConvertNodeToBean(
new ConversionToBeanContext(node), null, null);
return bean;
}
@Override
public <T> Iterable<T> findAll(final Class<T> beanType) {
return findByProperties(beanType, EMPTY_NAMES, EMPTY_VALUES);
}
@Override
public <T> Iterable<T> findAll(final StorageNode parentNode,
final Class<T> beanType) {
return findByProperties(parentNode, beanType, EMPTY_NAMES, EMPTY_VALUES);
}
@Override
public <T> Iterable<T> findByProperties(final Class<T> beanType,
final String[] propertyNames, final Object[] propertyValues) {
return findByProperties(null, beanType, propertyNames, propertyValues);
}
@Override
public <T> Iterable<T> findByProperties(final StorageNode parent,
final Class<T> beanType, final String[] propertyNames,
final Object[] propertyValues) {
try {
checkNotNull("currentPartition", currentPartition);
checkNotNull("currentSession", currentSession);
checkNotNull("beanType", beanType);
checkNotNull("propertyNames", propertyNames);
checkNotNull("propertyValues", propertyValues);
checkCondition("namesAndValues:sameSize",
propertyNames.length == propertyValues.length);
final NodeCriteriaBuilder builder = currentSession
.withPartition(currentPartition).createCriteria()
.withNodeType(internalGetNodeName(beanType));
final Map<String, PropertyDescriptor> allDescriptors = createMapWith(PropertyUtils
.getPropertyDescriptors(beanType));
for (int i = 0, size = propertyNames.length; i < size; i++) {
final PropertyDescriptor descriptor = allDescriptors
.get(propertyNames[i]);
if (descriptor == null) { throw new SLRuntimeException("invalid property:"
+ propertyNames[i]); }
builder.withProperty(propertyNames[i]).equalsTo(
Conversion.convert(propertyValues[i], String.class));
}
final Iterable<StorageNode> foundItems = builder.buildCriteria()
.andSearch(currentSession);
final IteratorBuilder.SimpleIteratorBuilder<T, StorageNode> b = IteratorBuilder
.<T, StorageNode>createIteratorBuilder();
b.withConverter(new IteratorBuilder.Converter<T, StorageNode>() {
@Override
public T convert(final StorageNode nodeEntry)
throws Exception {
return (T) convertNodeToBean(nodeEntry);
}
});
b.withReferee(new IteratorBuilder.NextItemReferee<StorageNode>() {
@Override
public boolean canAcceptAsNewItem(final StorageNode nodeEntry) {
final String typeAsString = nodeEntry.getPropertyValueAsString(
currentSession, NODE_ENTRY_TYPE);
if (typeAsString != null
&& typeAsString.equals(beanType.getName())) {
if (parent != null) {
StorageNode parentNode = nodeEntry;
while (parentNode != null) {
if (parentNode.getKey().equals(parent.getKey())) {
return true;
}
parentNode = parentNode
.getParent(currentSession);
}
return false;
}
return true;
}
return false;
}
});
final Iterable<T> result = b.withItems(foundItems).andBuild();
return result;
} catch (final Exception e) {
throw logAndReturnNew(e, SLRuntimeException.class);
}
}
@Override
public <T> T findUnique(final Class<T> beanType) {
return findUniqueByProperties(beanType, EMPTY_NAMES, EMPTY_VALUES);
}
@Override
public <T> T findUnique(final StorageNode parentNode,
final Class<T> beanType) {
return findUniqueByProperties(parentNode, beanType, EMPTY_NAMES,
EMPTY_VALUES);
}
@Override
public <T> T findUniqueByProperties(final Class<T> beanType,
final String[] propertyNames, final Object[] propertyValues) {
return findUniqueByProperties(null, beanType, propertyNames,
propertyValues);
}
@Override
public <T> T findUniqueByProperties(final StorageNode parent,
final Class<T> beanType, final String[] propertyNames,
final Object[] propertyValues) {
final Iterable<T> result = findByProperties(parent, beanType,
propertyNames, propertyValues);
final Iterator<T> it = result.iterator();
final T resultAsBean = it.hasNext() ? it.next() : null;
return resultAsBean;
}
@Override
public Partition getCurrentPartition() {
return currentPartition;
}
@Override
public StorageSession getCurrentSession() {
return currentSession;
}
@Override
public InternalMethods getInternalMethods() {
return internalMethods;
}
@Override
public StorageSession.PartitionMethods getPartitionMethods() {
return currentSession.withPartition(currentPartition);
}
}