package org.etk.orm.core; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Iterator; import java.util.List; import javax.jcr.RepositoryException; import org.etk.orm.api.ORMIOException; import org.etk.orm.api.Status; import org.etk.orm.plugins.common.CloneableInputStream; import org.etk.orm.plugins.common.jcr.Path; import org.etk.orm.plugins.instrument.MethodHandler; import org.etk.orm.plugins.jcr.NodeTypeInfo; import org.etk.orm.plugins.mapper.ObjectMapper; import org.etk.orm.plugins.vt2.ValueDefinition; public abstract class ObjectContext<O extends ObjectContext<O>> implements MethodHandler { public abstract ObjectMapper<O> getMapper(); public abstract Object getObject(); public abstract EntityContext getEntity(); /** * Returns the type info associated with the context. Null is returned when the context is in transient * state, otherwise the type info of the corresponding node is returned. * * @return the type info */ public abstract NodeTypeInfo getTypeInfo(); public abstract Status getStatus(); public abstract DomainSession getSession(); public final Object invoke(Object o, Method method) throws Throwable { MethodInvoker<O> invoker = getMapper().getInvoker(method); if (invoker != null) { return invoker.invoke((O)this); } else { throw createCannotInvokeError(method); } } public final Object invoke(Object o, Method method, Object arg) throws Throwable { MethodInvoker<O> invoker = getMapper().getInvoker(method); if (invoker != null) { return invoker.invoke((O)this, arg); } else { throw createCannotInvokeError(method, arg); } } public final Object invoke(Object o, Method method, Object[] args) throws Throwable { MethodInvoker<O> invoker = getMapper().getInvoker(method); if (invoker != null) { switch (args.length) { case 0: return invoker.invoke((O)this); case 1: return invoker.invoke((O)this, args[0]); default: return invoker.invoke((O)this, args); } } else { throw createCannotInvokeError(method, (Object[])args); } } private AssertionError createCannotInvokeError(Method method, Object... args) { StringBuilder msg = new StringBuilder("Cannot invoke method ").append(method.getName()).append("("); Class[] parameterTypes = method.getParameterTypes(); for (int i = 0;i < parameterTypes.length;i++) { if (i > 0) { msg.append(','); } msg.append(parameterTypes[i].getName()); } msg.append(") with arguments ("); for (int i = 0;i < args.length;i++) { if (i > 0) { msg.append(','); } msg.append(String.valueOf(args[i])); } msg.append(")"); return new AssertionError(msg); } public final <V> V getPropertyValue(String propertyName, ValueDefinition<?, V> type) throws RepositoryException { EntityContext ctx = getEntity(); EntityContextState state = ctx.state; // propertyName = state.getSession().domain.encodeName(ctx, propertyName, NameKind.PROPERTY); Path.validateName(propertyName); // NodeTypeInfo typeInfo = getTypeInfo(); return state.getPropertyValue(typeInfo, propertyName, type); } public final <V> List<V> getPropertyValues(String propertyName, ValueDefinition<?, V> simpleType, ListType listType) throws RepositoryException { EntityContext ctx = getEntity(); EntityContextState state = ctx.state; // propertyName = state.getSession().domain.encodeName(ctx, propertyName, NameKind.PROPERTY); Path.validateName(propertyName); // NodeTypeInfo typeInfo = getTypeInfo(); return state.getPropertyValues(typeInfo, propertyName, simpleType, listType); } public final <V> void setPropertyValue(String propertyName, ValueDefinition<?, V> type, V o) throws RepositoryException { EntityContext ctx = getEntity(); EntityContextState state = ctx.state; // propertyName = state.getSession().domain.encodeName(ctx, propertyName, NameKind.PROPERTY); Path.validateName(propertyName); // Object object = getObject(); // EventBroadcaster broadcaster = state.getSession().broadcaster; // NodeTypeInfo typeInfo = getTypeInfo(); // if (o instanceof InputStream && broadcaster.hasStateChangeListeners()) { CloneableInputStream in; try { in = new CloneableInputStream((InputStream)o); } catch (IOException e) { throw new ORMIOException("Could not read stream", e); } @SuppressWarnings("unchecked") V v = (V)in; state.setPropertyValue(typeInfo, propertyName, type, v); broadcaster.propertyChanged(state.getId(), object, propertyName, in.clone()); } else { state.setPropertyValue(typeInfo, propertyName, type, o); broadcaster.propertyChanged(state.getId(), object, propertyName, o); } } public final <V> void setPropertyValues(String propertyName, ValueDefinition<?, V> type, ListType listType, List<V> objects) throws RepositoryException { EntityContext ctx = getEntity(); EntityContextState state = ctx.state; // propertyName = state.getSession().domain.encodeName(ctx, propertyName, NameKind.PROPERTY); Path.validateName(propertyName); // NodeTypeInfo typeInfo = getTypeInfo(); // state.setPropertyValues(typeInfo, propertyName, type, listType, objects); } public final void removeChild(String prefix, String localName) { if (getStatus() != Status.PERSISTENT) { throw new IllegalStateException("Can only insert/remove a child of a persistent object"); } // getSession().removeChild(this, prefix, localName); } public final void orderBefore(EntityContext srcCtx, EntityContext dstCtx) { getSession().orderBefore(this, srcCtx, dstCtx); } public final <T1 extends Throwable, T2 extends Throwable> void addChild( ThrowableFactory<T1> thisStateTF, ThrowableFactory<T2> childStateTF, String prefix, EntityContext childCtx) throws T1, T2 { String localName = childCtx.getLocalName(); addChild(thisStateTF, childStateTF, prefix, localName, childCtx); } public final <T1 extends Throwable, T2 extends Throwable> void addChild( ThrowableFactory<T1> thisStateTF, ThrowableFactory<T2> childStateTF, String prefix, String localName, EntityContext childCtx) throws T1, T2 { if (childCtx.getStatus() == Status.PERSISTENT) { getSession().move(childStateTF, thisStateTF, childCtx, this, prefix, localName); } else { getSession().persist(thisStateTF, childStateTF, ThrowableFactory.NPE, this, childCtx, prefix, localName); } } public final EntityContext getChild(String prefix, String localName) { return getSession().getChild(this, prefix, localName); } public final <T> Iterator<T> getChildren(Class<T> filterClass) { return getSession().getChildren(this, filterClass); } }