/**
* 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.graph.internal;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;
import static org.openspotlight.common.Pair.newPair;
import static org.openspotlight.common.util.Conversion.convert;
import static org.openspotlight.common.util.Exceptions.logAndReturn;
import static org.openspotlight.common.util.Sha1.getNumericSha1Signature;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.beanutils.PropertyUtils;
import org.openspotlight.common.Pair;
import org.openspotlight.common.Pair.PairEqualsMode;
import org.openspotlight.common.exception.SLRuntimeException;
import org.openspotlight.common.util.Conversion;
import org.openspotlight.common.util.Equals;
import org.openspotlight.common.util.Exceptions;
import org.openspotlight.common.util.HashCodes;
import org.openspotlight.common.util.Reflection;
import org.openspotlight.common.util.SerializationUtil;
import org.openspotlight.common.util.Strings;
import org.openspotlight.graph.Context;
import org.openspotlight.graph.Element;
import org.openspotlight.graph.Link;
import org.openspotlight.graph.LinkDirection;
import org.openspotlight.graph.Node;
import org.openspotlight.graph.PropertyContainer;
import org.openspotlight.graph.TreeLineReference;
import org.openspotlight.graph.TreeLineReference.ArtifactLineReference;
import org.openspotlight.graph.TreeLineReference.SimpleLineReference;
import org.openspotlight.graph.annotation.DefineHierarchy;
import org.openspotlight.graph.annotation.InitialWeight;
import org.openspotlight.graph.annotation.IsMetaType;
import org.openspotlight.graph.annotation.LinkAutoBidirectional;
import org.openspotlight.graph.annotation.TransientProperty;
import org.openspotlight.storage.NodeKeyBuilderImpl;
import org.openspotlight.storage.Partition;
import org.openspotlight.storage.PartitionFactory;
import org.openspotlight.storage.StorageSession;
import org.openspotlight.storage.StringKeysSupport;
import org.openspotlight.storage.domain.StorageLink;
import org.openspotlight.storage.domain.StorageNode;
import org.openspotlight.storage.domain.key.NodeKey;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
public class NodeAndLinkSupport {
private static class LinkImpl extends Link
implements
PropertyContainerMetadata<StorageLink>,
PropertyContainerLineReferenceData {
private static final int SOURCE = 0;
private static final int TARGET = 1;
private WeakReference<StorageLink> cachedEntry;
private int count;
private LinkDirection linkDirection = LinkDirection.UNIDIRECTIONAL;
private final Class<? extends Link> linkType;
private final PropertyContainerImpl propertyContainerImpl;
private final Node[] sides = new Node[2];
public LinkImpl(final String id, final String linkName,
final Class<? extends Link> linkType,
final Map<String, Class<? extends Serializable>> propertyTypes,
final Map<String, Serializable> propertyValues,
final int initialWeigthValue, final int weightValue,
final Node source, final Node target,
final LinkDirection linkDirection) {
propertyContainerImpl = new PropertyContainerImpl(id,
linkType.getName(), propertyTypes, propertyValues,
initialWeigthValue, weightValue, source.getContextId());
sides[SOURCE] = source;
sides[TARGET] = target;
this.linkType = linkType;
this.linkDirection = linkDirection;
}
@Override
public int compareTo(final Link o) {
return getId().compareTo(o.getId());
}
@Override
public void createLineReference(final int beginLine, final int endLine,
final int beginColumn, final int endColumn,
final String statement, final String artifactId) {
propertyContainerImpl.createLineReference(beginLine, endLine,
beginColumn, endColumn, statement, artifactId);
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof Link)) { return false; }
final Link that = (Link) obj;
return getId().equals(that.getId());
}
@Override
public StorageLink getCached() {
return cachedEntry != null ? cachedEntry.get() : null;
}
@Override
public Iterable<ArtifactLineReference> getCachedLineReference(
final String artifactId) {
return propertyContainerImpl.getCachedLineReference(artifactId);
}
@Override
public String getContextId() {
return propertyContainerImpl.getContextId();
}
@Override
public int getCount() {
return count;
}
@Override
public String getId() {
return propertyContainerImpl.getId();
}
@Override
public final int getInitialWeightValue() {
return propertyContainerImpl.getInitialWeightValue();
}
@Override
public LinkDirection getDirection() {
return linkDirection;
}
@Override
public Class<? extends Link> getType() {
return linkType;
}
@Override
public Map<String, Map<String, Set<SimpleLineReference>>> getNewLineReferenceData() {
return propertyContainerImpl.getNewLineReferenceData();
}
@Override
public Node getOtherSide(final Node node)
throws IllegalArgumentException {
if (node.equals(sides[SOURCE])) { return sides[TARGET]; }
if (node.equals(sides[TARGET])) { return sides[SOURCE]; }
throw new IllegalArgumentException();
}
@Override
public Set<Pair<String, Serializable>> getProperties() {
return propertyContainerImpl.getProperties();
}
@Override
public PropertyContainerImpl getPropertyContainerImpl() {
return propertyContainerImpl;
}
@Override
public Iterable<String> getPropertyKeys() {
return propertyContainerImpl.getPropertyKeys();
}
@Override
public <V extends Serializable> V getPropertyValue(final String key) {
return propertyContainerImpl.<V>getPropertyValue(key);
}
@Override
public <V extends Serializable> V getPropertyValue(final String key,
final V defaultValue) {
return propertyContainerImpl.getPropertyValue(key, defaultValue);
}
@Override
public String getPropertyValueAsString(final String key) {
return propertyContainerImpl.getPropertyValueAsString(key);
}
@Override
public Node[] getSides() {
return new Node[] {sides[SOURCE], sides[TARGET]};
}
@Override
public Node getSource() {
return sides[SOURCE];
}
@Override
public Node getTarget() {
return sides[TARGET];
}
@Override
public String getTypeName() {
return propertyContainerImpl.getTypeName();
}
@Override
public final int getWeightValue() {
return propertyContainerImpl.getWeightValue();
}
@Override
public int hashCode() {
return getId().hashCode();
}
@Override
public boolean hasProperty(final String key)
throws IllegalArgumentException {
return propertyContainerImpl.hasProperty(key);
}
@Override
public boolean isBidirectional() {
return linkDirection.equals(LinkDirection.BIDIRECTIONAL);
}
@Override
public boolean isDirty() {
return propertyContainerImpl.isDirty();
}
@Override
public void removeProperty(final String key) {
propertyContainerImpl.removeProperty(key);
}
// public void resetDirtyFlag() {
// propertyContainerImpl.resetDirtyFlag();
// }
@Override
public void setCached(final StorageLink entry) {
cachedEntry = new WeakReference<StorageLink>(
entry);
}
@Override
public void setCachedLineReference(final String artifactId,
final Iterable<ArtifactLineReference> newLineReference) {
propertyContainerImpl.setCachedLineReference(artifactId,
newLineReference);
}
@Override
public void setCount(final int value) {
count = value;
propertyContainerImpl.markAsDirty();
}
@Override
public <V extends Serializable> void setProperty(final String key,
final V value)
throws IllegalArgumentException {
propertyContainerImpl.setProperty(key, value);
}
@Override
public String toString() {
return "<" + linkDirection.name().substring(0, 3) + "> Link["
+ getId() + "]";
}
}
private static class NodeImpl extends Node
implements
PropertyContainerMetadata<StorageNode>,
PropertyContainerLineReferenceData {
private WeakReference<StorageNode> cachedEntry;
private String caption;
private final String contextId;
private volatile int hashCode = 0;
private final String name;
private final BigInteger numericType;
private final String parentId;
private final PropertyContainerImpl propertyContainerImpl;
private NodeImpl(final String name, final Class<? extends Node> type,
final String id,
final Map<String, Class<? extends Serializable>> propertyTypes,
final Map<String, Serializable> propertyValues,
final String parentId, final String contextId,
final int weightValue) {
propertyContainerImpl = new PropertyContainerImpl(id,
type.getName(), propertyTypes, propertyValues,
findInitialWeight(type), weightValue, contextId);
this.name = name;
numericType = findNumericType(type);
this.parentId = parentId;
this.contextId = contextId;
}
@Override
public int compareTo(final Node o) {
return getId().compareTo(o.getId());
}
@Override
public void createLineReference(final int beginLine, final int endLine,
final int beginColumn, final int endColumn,
final String statement, final String artifactId) {
propertyContainerImpl.createLineReference(beginLine, endLine,
beginColumn, endColumn, statement, artifactId);
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof Node)) { return false; }
final Node slnode = (Node) obj;
final boolean result = getId().equals(slnode.getId())
&& Equals.eachEquality(getParentId(), slnode.getParentId())
&& Equals.eachEquality(getContextId(),
slnode.getContextId());
return result;
}
@Override
public StorageNode getCached() {
return cachedEntry != null ? cachedEntry.get() : null;
}
@Override
public Iterable<ArtifactLineReference> getCachedLineReference(
final String artifactId) {
return propertyContainerImpl.getCachedLineReference(artifactId);
}
@Override
public String getCaption() {
return caption;
}
@Override
public String getContextId() {
return contextId;
}
@Override
public String getId() {
return propertyContainerImpl.getId();
}
@Override
public final int getInitialWeightValue() {
return propertyContainerImpl.getInitialWeightValue();
}
@Override
public String getName() {
return name;
}
@Override
public Map<String, Map<String, Set<SimpleLineReference>>> getNewLineReferenceData() {
return propertyContainerImpl.getNewLineReferenceData();
}
@Override
public BigInteger getNumericType() {
return numericType;
}
@Override
public String getParentId() {
return parentId;
}
@Override
public Set<Pair<String, Serializable>> getProperties() {
return propertyContainerImpl.getProperties();
}
@Override
public PropertyContainerImpl getPropertyContainerImpl() {
return propertyContainerImpl;
}
@Override
public Iterable<String> getPropertyKeys() {
return propertyContainerImpl.getPropertyKeys();
}
@Override
public <V extends Serializable> V getPropertyValue(final String key) {
return propertyContainerImpl.<V>getPropertyValue(key);
}
@Override
public <V extends Serializable> V getPropertyValue(final String key,
final V defaultValue) {
return propertyContainerImpl
.<V>getPropertyValue(key, defaultValue);
}
@Override
public String getPropertyValueAsString(final String key) {
return propertyContainerImpl.getPropertyValueAsString(key);
}
@Override
public String getTypeName() {
return propertyContainerImpl.getTypeName();
}
@Override
public final int getWeightValue() {
return propertyContainerImpl.getWeightValue();
}
@Override
public int hashCode() {
int result = hashCode;
if (result == 0) {
result = HashCodes.hashOf(getId(), getParentId(),
getContextId());
hashCode = result;
}
return result;
}
@Override
public boolean hasProperty(final String key)
throws IllegalArgumentException {
return propertyContainerImpl.hasProperty(key);
}
@Override
public boolean isDirty() {
return propertyContainerImpl.isDirty();
}
@Override
public void removeProperty(final String key) {
propertyContainerImpl.removeProperty(key);
}
// public void resetDirtyFlag() {
// propertyContainerImpl.resetDirtyFlag();
// }
@Override
public void setCached(
final StorageNode entry) {
cachedEntry = new WeakReference<StorageNode>(
entry);
}
@Override
public void setCachedLineReference(final String artifactId,
final Iterable<ArtifactLineReference> newLineReference) {
propertyContainerImpl.setCachedLineReference(artifactId,
newLineReference);
}
@Override
public void setCaption(final String caption) {
this.caption = caption;
}
@Override
public <V extends Serializable> void setProperty(final String key,
final V value)
throws IllegalArgumentException {
propertyContainerImpl.setProperty(key, value);
}
@Override
public String toString() {
return getName() + ":" + getId();
}
}
private static class PropertyContainerInterceptor implements
MethodInterceptor {
private static enum MethodType {
GETTER,
OTHER,
SETTER
}
private final PropertyContainer internalPropertyContainerImpl;
public PropertyContainerInterceptor(
final PropertyContainer propertyContainerImpl) {
internalPropertyContainerImpl = propertyContainerImpl;
}
private MethodType getMethodType(final String methodName,
final Method method) {
if (method.isAnnotationPresent(TransientProperty.class)) { return MethodType.OTHER; }
if (methodName.startsWith("set")
&& method.getParameterTypes().length == 1
&& internalPropertyContainerImpl.hasProperty(methodName
.substring(3))) {
return MethodType.SETTER;
} else if (methodName.startsWith("get")
&& method.getParameterTypes().length == 0
&& internalPropertyContainerImpl.hasProperty(methodName
.substring(3))) {
return MethodType.GETTER;
} else if (methodName.startsWith("is")
&& method.getParameterTypes().length == 0
&& internalPropertyContainerImpl.hasProperty(methodName
.substring(2))) { return MethodType.GETTER; }
return MethodType.OTHER;
}
private Serializable invokeGetter(final String methodName) {
final String propertyName = methodName.startsWith("get") ? Strings
.firstLetterToLowerCase(methodName.substring(3)) : Strings
.firstLetterToLowerCase(methodName.substring(2));// is
return internalPropertyContainerImpl.getPropertyValue(propertyName);
}
private Object invokeSetter(final Object obj, final String methodName,
final Method method, final Object[] args,
final MethodProxy methodProxy)
throws Throwable {
internalPropertyContainerImpl.setProperty(methodName.substring(3),
(Serializable) args[0]);
return null;
}
@Override
public Object intercept(final Object obj, final Method method,
final Object[] args, final MethodProxy proxy)
throws Throwable {
final Class<?> declarringClass = method.getDeclaringClass();
final boolean methodFromSuperClasses = declarringClass
.equals(Node.class)
|| declarringClass.equals(Link.class)
|| declarringClass.equals(PropertyContainerImpl.class)
|| declarringClass.isInterface()
|| declarringClass.equals(Object.class);
final String methodName = method.getName();
if (methodFromSuperClasses) {
return method.invoke(internalPropertyContainerImpl, args);
} else {
switch (getMethodType(methodName, method)) {
case GETTER:
return invokeGetter(methodName);
case SETTER:
return invokeSetter(obj, methodName, method, args, proxy);
}
return proxy.invokeSuper(obj, args);
}
}
}
private static interface PropertyContainerLineReferenceData {
Iterable<ArtifactLineReference> getCachedLineReference(String artifactId);
Map<String, Map<String, Set<SimpleLineReference>>> getNewLineReferenceData();
void setCachedLineReference(String artifactId,
Iterable<ArtifactLineReference> newLineReference);
}
public static class PropertyContainerImpl implements Element,
PropertyContainerLineReferenceData {
private final String contextId;
private final AtomicBoolean dirty;
private final String id;
private final int initialWeightValue;
// ArtifactId,Statement,lineData
private final Map<String, Map<String, Set<SimpleLineReference>>> lineReferenceNewData =
new HashMap<String, Map<String, Set<SimpleLineReference>>>();
private final Map<String, Class<? extends Serializable>> propertyTypes;
private final Map<String, Serializable> propertyValues;
private final Set<String> removedProperties;
private SoftReference<Map<String, Iterable<ArtifactLineReference>>> treeLineReference;
private final String typeName;
private final int weightValue;
public PropertyContainerImpl(final String id, final String typeName,
final Map<String, Class<? extends Serializable>> propertyTypes,
final Map<String, Serializable> propertyValues,
final int initialWeigthValue, final int weightValue,
final String contextId) {
dirty = new AtomicBoolean();
this.typeName = typeName;
initialWeightValue = initialWeigthValue;
this.weightValue = weightValue;
this.id = id;
this.propertyTypes = propertyTypes;
this.propertyValues = propertyValues;
removedProperties = newHashSet();
this.contextId = contextId;
}
@Override
public void createLineReference(final int beginLine, final int endLine,
final int beginColumn, final int endColumn,
final String statement, final String artifactId) {
Map<String, Set<SimpleLineReference>> artifactEntry = lineReferenceNewData
.get(artifactId);
if (artifactEntry == null) {
artifactEntry = new HashMap<String, Set<SimpleLineReference>>();
lineReferenceNewData.put(artifactId, artifactEntry);
}
Set<SimpleLineReference> statementEntry = artifactEntry
.get(statement);
if (statementEntry == null) {
statementEntry = new HashSet<SimpleLineReference>();
artifactEntry.put(statement, statementEntry);
}
statementEntry.add(TreeLineReferenceSupport
.createSimpleLineReference(beginLine, endLine, beginColumn,
endColumn));
dirty.set(true);
}
@Override
public Iterable<ArtifactLineReference> getCachedLineReference(
final String artifactId) {
final Map<String, Iterable<ArtifactLineReference>> cache = treeLineReference == null ? null
: treeLineReference.get();
if (cache == null) { return null; }
if (artifactId != null) { return cache.get(artifactId); }
if (cache.isEmpty()) { return null; }
final Builder<ArtifactLineReference> builder = ImmutableSet.builder();
for (final Iterable<ArtifactLineReference> val: cache.values()) {
for (final ArtifactLineReference r: val) {
builder.add(r);
}
}
return builder.build();
}
@Override
public String getContextId() {
return contextId;
}
@Override
public String getId() {
return id;
}
@Override
public final int getInitialWeightValue() {
return initialWeightValue;
}
@Override
public Map<String, Map<String, Set<SimpleLineReference>>> getNewLineReferenceData() {
return lineReferenceNewData;
}
@Override
public Set<Pair<String, Serializable>> getProperties() {
final ImmutableSet.Builder<Pair<String, Serializable>> builder = ImmutableSet
.builder();
for (final Map.Entry<String, ? extends Serializable> entry: propertyValues
.entrySet()) {
builder.add(newPair(entry.getKey(),
(Serializable) entry.getValue(), PairEqualsMode.K1));
}
return builder.build();
}
@Override
public Iterable<String> getPropertyKeys() {
return ImmutableSet.copyOf(propertyTypes.keySet());
}
@SuppressWarnings("unchecked")
@Override
public <V extends Serializable> V getPropertyValue(final String key) {
return (V) propertyValues.get(key);
}
@SuppressWarnings("unchecked")
@Override
public <V extends Serializable> V getPropertyValue(final String key,
final V defaultValue) {
final V value = (V) propertyValues.get(key);
return value == null ? defaultValue : value;
}
@Override
public String getPropertyValueAsString(final String key) {
return convert(propertyValues.get(key), String.class);
}
@Override
public String getTypeName() {
return typeName;
}
@Override
public final int getWeightValue() {
return weightValue;
}
@Override
public boolean hasProperty(final String key)
throws IllegalArgumentException {
return propertyTypes.containsKey(Strings
.firstLetterToLowerCase(key));
}
@Override
public boolean isDirty() {
return dirty.get();
}
public void markAsDirty() {
dirty.set(true);
}
@Override
public void removeProperty(String key) {
key = Strings.firstLetterToLowerCase(key);
propertyTypes.remove(key);
propertyValues.remove(key);
removedProperties.add(key);
dirty.set(true);
}
public void resetDirtyFlag() {
dirty.set(false);
removedProperties.clear();
}
@Override
public void setCachedLineReference(final String artifactId,
final Iterable<ArtifactLineReference> newLineReference) {
Map<String, Iterable<ArtifactLineReference>> cache = treeLineReference == null ? null
: treeLineReference.get();
if (cache == null) {
cache = new HashMap<String, Iterable<ArtifactLineReference>>();
treeLineReference = new SoftReference<Map<String, Iterable<ArtifactLineReference>>>(
cache);
}
cache.put(artifactId, newLineReference);
}
@Override
public <V extends Serializable> void setProperty(String key,
final V value)
throws IllegalArgumentException {
key = Strings.firstLetterToLowerCase(key);
if (!hasProperty(key)) { throw logAndReturn(new IllegalArgumentException(
"invalid property key " + key + " for type "
+ getTypeName())); }
final Class<? extends Serializable> propType = propertyTypes
.get(key);
if (value != null) {
final Class<?> valueType = Reflection
.findClassWithoutPrimitives(value.getClass());
if (!valueType.isAssignableFrom(propType)) { throw logAndReturn(new IllegalArgumentException(
"invalid property type "
+ value.getClass().getName() + " for type "
+ getTypeName() + " (should be "
+ propertyTypes.get(key).getName() + ")"));
}
}
propertyValues.put(key, value);
dirty.set(true);
}
}
public static interface PropertyContainerMetadata<T> {
public T getCached();
public PropertyContainerImpl getPropertyContainerImpl();
public void setCached(T entry);
}
private static final String LINEREF_SUFIX = "_lineRef";
public static final String BIDIRECTIONAL_LINK_IDS = "__bidirectional_link_ids";
public static final String CAPTION = "__node_caption";
public static final String CORRECT_CLASS = "__node_concrete_class";
public static final String LINK_DIRECTION = "__link_direction";
public static final String NAME = "__node_name";
public static final String NODE_ID = "__node_weigth_value";
public static final String NUMERIC_TYPE = "__node_numeric_type";
public static final String WEIGTH_VALUE = "__node_weigth_value";
private static void fixTypeData(final StorageSession session,
final Class<? extends Node> clazz,
final StorageNode node) {
final String numericTypeAsString = node.getPropertyValueAsString(session,
NUMERIC_TYPE);
final BigInteger numericTypeFromTargetNodeType = findNumericType(clazz);
if (numericTypeAsString != null) {
final BigInteger numericTypeAsBigInteger = new BigInteger(
numericTypeAsString);
if (numericTypeFromTargetNodeType
.compareTo(numericTypeAsBigInteger) > 0) {
setWeigthAndTypeOnNode(session, node, clazz,
numericTypeFromTargetNodeType);
}
} else {
setWeigthAndTypeOnNode(session, node, clazz,
numericTypeFromTargetNodeType);
}
}
private static BigInteger numericTypeFromClass(
final Class<? extends Node> currentType) {
return getNumericSha1Signature(currentType.getName());
}
private static void setWeigthAndTypeOnNode(final StorageSession session,
final StorageNode node,
final Class<? extends Node> type,
final BigInteger weightFromTargetNodeType) {
node.setIndexedProperty(session, NUMERIC_TYPE,
weightFromTargetNodeType.toString());
node.setIndexedProperty(session, CORRECT_CLASS, type.getName());
}
@SuppressWarnings("unchecked")
public static <T extends Link> T createLink(final PartitionFactory factory,
final StorageSession session, final Class<T> clazz,
final Node rawOrigin, final Node rawTarget,
final LinkDirection direction, final boolean createIfDontExists) {
final Map<String, Class<? extends Serializable>> propertyTypes = newHashMap();
final Map<String, Serializable> propertyValues = newHashMap();
final PropertyDescriptor[] descriptors = PropertyUtils
.getPropertyDescriptors(clazz);
StorageLink linkEntry = null;
Node origin, target;
if (rawOrigin.compareTo(rawTarget) == 0) { throw new IllegalStateException(); }
if (LinkDirection.BIDIRECTIONAL.equals(direction)
&& rawOrigin.compareTo(rawTarget) < 0) {
origin = rawTarget;
target = rawOrigin;
} else {
origin = rawOrigin;
target = rawTarget;
}
String linkId = null;
if (session != null) {
final StorageNode originAsSTNode = session
.getNode(origin.getId());
final StorageNode targetAsSTNode = session
.getNode(target.getId());
if (originAsSTNode == null && createIfDontExists) { throw new IllegalStateException(); }
if (originAsSTNode != null) {
if (clazz.isAnnotationPresent(LinkAutoBidirectional.class)
&& LinkDirection.UNIDIRECTIONAL.equals(direction)) {
final StorageLink possibleLink = session.getLink(targetAsSTNode,
originAsSTNode, clazz.getName());
final StorageLink anotherPossibleLink = session.getLink(
originAsSTNode, targetAsSTNode, clazz.getName());
if (possibleLink != null && anotherPossibleLink != null) { throw new IllegalStateException(); }
if (possibleLink != null
&& possibleLink.getPropertyValueAsString(session,
LINK_DIRECTION).equals(
LinkDirection.BIDIRECTIONAL.name())) {
return createLink(factory, session, clazz, rawOrigin,
rawTarget, LinkDirection.BIDIRECTIONAL,
createIfDontExists);
} else if (anotherPossibleLink != null
&& anotherPossibleLink.getPropertyValueAsString(session,
LINK_DIRECTION).equals(
LinkDirection.BIDIRECTIONAL.name())) {
return createLink(factory, session, clazz, rawTarget,
rawOrigin, LinkDirection.BIDIRECTIONAL,
createIfDontExists);
} else if (possibleLink != null) {
if (createIfDontExists) {
session.removeLink(possibleLink);
}
return createLink(factory, session, clazz, rawOrigin,
rawTarget, LinkDirection.BIDIRECTIONAL,
createIfDontExists);
} else if (anotherPossibleLink != null) {
if (createIfDontExists) {
session.removeLink(anotherPossibleLink);
}
return createLink(factory, session, clazz, rawOrigin,
rawTarget, LinkDirection.BIDIRECTIONAL,
createIfDontExists);
}
}
linkEntry = session.getLink(originAsSTNode, targetAsSTNode,
clazz.getName());
if (linkEntry == null) {
if (createIfDontExists) {
linkEntry = session.addLink(originAsSTNode,
targetAsSTNode, clazz.getName());
}
if (linkEntry != null) {
if (LinkDirection.BIDIRECTIONAL.equals(direction)) {
final InputStream objectAsStream = targetAsSTNode
.getPropertyValueAsStream(session,
BIDIRECTIONAL_LINK_IDS);
List<String> linkIds;
if (objectAsStream != null) {
linkIds = SerializationUtil
.deserialize(objectAsStream);
} else {
linkIds = new ArrayList<String>();
}
linkIds.add(linkEntry.getKeyAsString());
targetAsSTNode.setSimpleProperty(session,
BIDIRECTIONAL_LINK_IDS,
SerializationUtil.serialize(linkIds));
targetAsSTNode.setSimpleProperty(session,
LINK_DIRECTION,
LinkDirection.BIDIRECTIONAL.name());
}
}
}
}
linkId = StringKeysSupport.buildLinkKeyAsString(clazz.getName(), origin.getId(), target.getId());
}
for (final PropertyDescriptor d: descriptors) {
if (d.getName().equals("class")) {
continue;
}
propertyTypes.put(d.getName(),
(Class<? extends Serializable>) Reflection
.findClassWithoutPrimitives(d.getPropertyType()));
final Object rawValue = linkEntry != null ? linkEntry
.getPropertyValueAsString(session, d.getName()) : null;
final Serializable value = (Serializable) (rawValue != null ? Conversion
.convert(rawValue, d.getPropertyType()) : null);
propertyValues.put(d.getName(), value);
}
int weigthValue;
final Set<String> stNodeProperties = linkEntry != null ? linkEntry
.getPropertyNames(session) : Collections.<String>emptySet();
if (stNodeProperties.contains(WEIGTH_VALUE)) {
weigthValue = Conversion.convert(
linkEntry.getPropertyValueAsString(session, WEIGTH_VALUE),
Integer.class);
} else {
weigthValue = findInitialWeight(clazz);
}
final LinkImpl internalLink = new LinkImpl(linkId, clazz.getName(),
clazz, propertyTypes, propertyValues, findInitialWeight(clazz),
weigthValue, origin, target, direction);
if (linkEntry != null) {
internalLink.setCached(linkEntry);
internalLink.linkDirection = direction;
}
final Enhancer e = new Enhancer();
e.setSuperclass(clazz);
e.setInterfaces(new Class<?>[] {PropertyContainerMetadata.class});
e.setCallback(new PropertyContainerInterceptor(internalLink));
return (T) e.create(new Class[0], new Object[0]);
}
@SuppressWarnings("unchecked")
public static <T extends Node> T createNode(
final PartitionFactory factory,
final StorageSession session,
final String contextId,
final String parentId,
final Class<T> clazz,
final String name,
final boolean needsToVerifyType,
final Iterable<Class<? extends Link>> linkTypesForLinkDeletion,
final Iterable<Class<? extends Link>> linkTypesForLinkedNodeDeletion) {
final Map<String, Class<? extends Serializable>> propertyTypes = newHashMap();
final Map<String, Serializable> propertyValues = newHashMap();
final PropertyDescriptor[] descriptors = PropertyUtils
.getPropertyDescriptors(clazz);
StorageNode node = null;
if (contextId == null) { throw new IllegalStateException(); }
final Partition partition = factory.getPartition(contextId);
NodeKey internalNodeKey;
final Class<? extends Node> targetNodeType = findTargetClass(clazz);
if (session != null) {
internalNodeKey = session.withPartition(partition)
.createNodeKeyWithType(targetNodeType.getName())
.withSimpleKey(NAME, name).andCreate();
node = session.withPartition(partition).createCriteria()
.withUniqueKey(internalNodeKey).buildCriteria()
.andSearchUnique(session);
} else {
internalNodeKey = new NodeKeyBuilderImpl(targetNodeType.getName(), partition).withSimpleKey(NAME, name).andCreate();
}
for (final PropertyDescriptor d: descriptors) {
if (d.getName().equals("class")) {
continue;
}
propertyTypes.put(d.getName(),
(Class<? extends Serializable>) Reflection
.findClassWithoutPrimitives(d.getPropertyType()));
final Object rawValue = node != null ? node.getPropertyValueAsString(
session, d.getName()) : null;
final Serializable value = (Serializable) (rawValue != null ? Conversion
.convert(rawValue, d.getPropertyType()) : null);
propertyValues.put(d.getName(), value);
}
int weigthValue;
final Set<String> stNodeProperties = node != null ? node
.getPropertyNames(session) : Collections.<String>emptySet();
if (stNodeProperties.contains(WEIGTH_VALUE)) {
weigthValue = Conversion.convert(
node.getPropertyValueAsString(session, WEIGTH_VALUE),
Integer.class);
} else {
weigthValue = findInitialWeight(clazz);
}
Class<? extends Node> savedClass = null;
if (stNodeProperties.contains(CORRECT_CLASS)) {
savedClass = Conversion.convert(
node.getPropertyValueAsString(session, CORRECT_CLASS),
Class.class);
}
final BigInteger savedClassNumericType = savedClass != null ? findNumericType(savedClass)
: null;
final BigInteger proposedClassNumericType = findNumericType(clazz);
final Class<? extends Node> classToUse = savedClassNumericType != null
&& savedClassNumericType.compareTo(proposedClassNumericType) > 0 ? savedClass
: clazz;
final NodeImpl internalNode = new NodeImpl(name, classToUse,
internalNodeKey.getKeyAsString(), propertyTypes,
propertyValues, parentId, contextId, weigthValue);
if (node != null) {
internalNode.cachedEntry = new WeakReference<StorageNode>(
node);
if (needsToVerifyType) {
fixTypeData(session, classToUse, node);
}
final String captionAsString = node.getPropertyValueAsString(session,
CAPTION);
if (captionAsString != null) {
internalNode.setCaption(captionAsString);
}
}
final Enhancer e = new Enhancer();
e.setSuperclass(classToUse);
e.setInterfaces(new Class<?>[] {PropertyContainerMetadata.class});
e.setCallback(new PropertyContainerInterceptor(internalNode));
return (T) e.create(new Class[0], new Object[0]);
}
public static int findInitialWeight(final Class<?> clazz) {
return clazz.getAnnotation(InitialWeight.class).value();
}
@SuppressWarnings("unchecked")
public static BigInteger findNumericType(final Class<? extends Node> type) {
Class<?> currentType = type;
int depth = 0;
while (currentType != null) {
if (!Node.class.isAssignableFrom(currentType)) { throw logAndReturn(new IllegalStateException(
"No SLNode inherited type found with annotation "
+ DefineHierarchy.class.getSimpleName())); }
if (currentType.isAnnotationPresent(DefineHierarchy.class)) { return numericTypeFromClass(
(Class<? extends Node>) currentType)
.add(BigInteger.valueOf(depth)); }
currentType = currentType.getSuperclass();
depth++;
}
throw logAndReturn(new IllegalStateException(
"No SLNode inherited type found with annotation "
+ DefineHierarchy.class.getSimpleName() + " for type"
+ type));
}
@SuppressWarnings("unchecked")
public static Class<? extends Node> findTargetClass(final Class<?> type) {
Class<?> currentType = type;
while (currentType != null) {
if (!Node.class.isAssignableFrom(currentType)) { throw logAndReturn(new IllegalStateException(
"No SLNode inherited type found with annotation "
+ DefineHierarchy.class.getSimpleName())); }
if (currentType.isAnnotationPresent(DefineHierarchy.class)) { return (Class<? extends Node>) currentType; }
currentType = currentType.getSuperclass();
}
throw logAndReturn(new IllegalStateException(
"No SLNode inherited type found with annotation "
+ DefineHierarchy.class.getSimpleName()));
}
public static TreeLineReference getTreeLineReferences(
final StorageSession session, final PartitionFactory factory,
final Element e,
final String artifactId) {
final PropertyContainerImpl asPropertyContainer = ((PropertyContainerMetadata<?>) e)
.getPropertyContainerImpl();
Iterable<ArtifactLineReference> cached = asPropertyContainer
.getCachedLineReference(artifactId);
if (cached == null) {
final Partition lineRefPartition = factory.getPartition(e
.getContextId() + LINEREF_SUFIX);
final StorageNode lineRefNode = session.withPartition(lineRefPartition)
.createNewSimpleNode(e.getId());
final Set<String> artifactIds = artifactId != null ? ImmutableSet
.of(artifactId) : lineRefNode.getPropertyNames(session);
final Map<String, Iterable<ArtifactLineReference>> newCacheData =
new HashMap<String, Iterable<ArtifactLineReference>>();
for (final String currentArtifactId: artifactIds) {
final InputStream stream = lineRefNode.getPropertyValueAsStream(session,
currentArtifactId);
if (stream != null) {
final ArtifactLineReference artifactLineReference = SerializationUtil
.deserialize(stream);
final ImmutableSet<ArtifactLineReference> set = ImmutableSet
.of(artifactLineReference);
newCacheData.put(currentArtifactId, set);
asPropertyContainer.setCachedLineReference(
currentArtifactId, set);
}
}
cached = TreeLineReferenceSupport.copyOf(
lineRefNode.getKeyAsString(), cached,
asPropertyContainer.lineReferenceNewData, artifactId)
.getArtifacts();
asPropertyContainer.setCachedLineReference(artifactId, cached);
}
return TreeLineReferenceSupport.createTreeLineReference(e.getId(),
cached);
}
public static boolean isMetanode(final Class<? extends Node> clazz) {
return clazz.isAnnotationPresent(IsMetaType.class);
}
@SuppressWarnings("unchecked")
public static StorageNode retrievePreviousNode(
final PartitionFactory factory,
final StorageSession session,
final Context context, final Node node,
final boolean needsToVerifyType) {
try {
final PropertyContainerMetadata<StorageNode> metadata =
(PropertyContainerMetadata<StorageNode>) node;
StorageNode internalNode = metadata
.getCached();
if (internalNode == null) {
final Partition partition = factory.getPartition(context
.getId());
internalNode = session
.withPartition(partition)
.createNodeWithType(
findTargetClass(node.getClass()).getName())
.withSimpleKey(NAME, node.getName())
.withParent(node.getParentId()).andCreate();
if (needsToVerifyType) {
fixTypeData(session, (Class<? extends Node>) node
.getClass().getSuperclass(), internalNode);
}
metadata.setCached(internalNode);
}
internalNode
.setIndexedProperty(session, CAPTION, node.getCaption());
for (final String propName: node.getPropertyKeys()) {
final Serializable value = node.getPropertyValue(propName);
if (!PropertyUtils.getPropertyDescriptor(node, propName)
.getReadMethod()
.isAnnotationPresent(TransientProperty.class)) {
internalNode.setIndexedProperty(session, propName,
Conversion.convert(value, String.class));
}
}
return internalNode;
} catch (final Exception e) {
throw Exceptions.logAndReturnNew(e, SLRuntimeException.class);
}
}
public static void writeTreeLineReference(final StorageSession session,
final PartitionFactory factory, final Element e) {
final TreeLineReference treeLineReferences = getTreeLineReferences(session,
factory, e, null);
final Partition lineRefPartition = factory.getPartition(e
.getContextId() + LINEREF_SUFIX);
final StorageNode lineRefNode = session.withPartition(lineRefPartition)
.createNewSimpleNode(e.getId());
for (final ArtifactLineReference artifactLineReference: treeLineReferences
.getArtifacts()) {
lineRefNode.setSimpleProperty(session,
artifactLineReference.getArtifactId(),
SerializationUtil.serialize(artifactLineReference));
}
}
}