http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/StateResolver.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/StateResolver.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/StateResolver.java new file mode 100644 index 0000000..64bc165 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/StateResolver.java @@ -0,0 +1,40 @@ +/* + * 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.polygene.runtime.composite; + +import java.util.Map; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.property.PropertyDescriptor; + +/** + * StateResolver. + */ +public interface StateResolver +{ + Object getPropertyState( PropertyDescriptor propertyDescriptor ); + + EntityReference getAssociationState( AssociationDescriptor associationDescriptor ); + + Stream<EntityReference> getManyAssociationState( AssociationDescriptor associationDescriptor ); + + Stream<Map.Entry<String, EntityReference>> getNamedAssociationState( AssociationDescriptor associationDescriptor ); +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SynchronizedCompositeMethodInstancePool.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SynchronizedCompositeMethodInstancePool.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SynchronizedCompositeMethodInstancePool.java new file mode 100644 index 0000000..01025cf --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/SynchronizedCompositeMethodInstancePool.java @@ -0,0 +1,50 @@ +/* + * 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.polygene.runtime.composite; + +/** + * Method instance pool that keeps a linked list. Uses synchronization + * to ensure that instances are acquired and returned in a thread-safe + * manner. + */ +public final class SynchronizedCompositeMethodInstancePool + implements InstancePool<CompositeMethodInstance> +{ + private CompositeMethodInstance first = null; + + @Override + public synchronized CompositeMethodInstance obtainInstance() + { + CompositeMethodInstance instance = first; + if( instance != null ) + { + first = instance.getNext(); + } + return instance; + } + + @Override + public synchronized void releaseInstance( CompositeMethodInstance instance ) + { + instance.setNext( first ); + first = instance; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientBuilderInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientBuilderInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientBuilderInstance.java new file mode 100644 index 0000000..a622a57 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientBuilderInstance.java @@ -0,0 +1,102 @@ +/* + * 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.polygene.runtime.composite; + +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.composite.CompositeInstance; +import org.apache.polygene.api.composite.TransientBuilder; +import org.apache.polygene.api.composite.TransientDescriptor; +import org.apache.polygene.runtime.property.PropertyInstance; + +/** + * JAVADOC + */ +public final class TransientBuilderInstance<T> + implements TransientBuilder<T> +{ + private TransientModel model; + + // lazy initialized in accessor + private UsesInstance uses = UsesInstance.EMPTY_USES; + + // lazy initialized in accessor + private CompositeInstance prototypeInstance; + + private TransientStateInstance state; + + public TransientBuilderInstance( TransientDescriptor model, + TransientStateInstance state, + UsesInstance uses + ) + { + this.model = (TransientModel) model; + this.state = state; + this.uses = uses; + } + + @Override + public TransientBuilder<T> use( Object... usedObjects ) + { + uses = uses.use( usedObjects ); + return this; + } + + @Override + public T prototype() + { + // Instantiate given value type + if( prototypeInstance == null ) + { + prototypeInstance = model.newInstance( uses, state ); + } + + return prototypeInstance.<T>proxy(); + } + + @Override + public <K> K prototypeFor( Class<K> mixinType ) + { + // Instantiate given value type + if( prototypeInstance == null ) + { + prototypeInstance = model.newInstance( uses, state ); + } + + return prototypeInstance.newProxy( mixinType ); + } + + @Override + public T newInstance() + throws ConstructionException + { + // Set correct info's (immutable) on the state + model.state().properties() + .forEach( + propertyDescriptor -> + ( (PropertyInstance<Object>) state.propertyFor( propertyDescriptor.accessor() ) ) + .setPropertyInfo( propertyDescriptor ) ); + + model.checkConstraints( state ); + + CompositeInstance compositeInstance = model.newInstance( uses, state ); + return compositeInstance.<T>proxy(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientClassLoader.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientClassLoader.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientClassLoader.java new file mode 100644 index 0000000..c909e33 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientClassLoader.java @@ -0,0 +1,789 @@ +/* + * 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.polygene.runtime.composite; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import org.apache.polygene.api.entity.Lifecycle; +import org.apache.polygene.api.mixin.Initializable; +import org.apache.polygene.api.util.Methods; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import static org.apache.polygene.api.util.Classes.RAW_CLASS; +import static org.apache.polygene.api.util.Classes.interfacesOf; +import static org.objectweb.asm.Opcodes.AASTORE; +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ACC_SUPER; +import static org.objectweb.asm.Opcodes.ACONST_NULL; +import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.ANEWARRAY; +import static org.objectweb.asm.Opcodes.ARETURN; +import static org.objectweb.asm.Opcodes.ASTORE; +import static org.objectweb.asm.Opcodes.ATHROW; +import static org.objectweb.asm.Opcodes.BIPUSH; +import static org.objectweb.asm.Opcodes.CHECKCAST; +import static org.objectweb.asm.Opcodes.DLOAD; +import static org.objectweb.asm.Opcodes.DRETURN; +import static org.objectweb.asm.Opcodes.DUP; +import static org.objectweb.asm.Opcodes.FLOAD; +import static org.objectweb.asm.Opcodes.FRETURN; +import static org.objectweb.asm.Opcodes.GETFIELD; +import static org.objectweb.asm.Opcodes.GETSTATIC; +import static org.objectweb.asm.Opcodes.GOTO; +import static org.objectweb.asm.Opcodes.ICONST_0; +import static org.objectweb.asm.Opcodes.ICONST_1; +import static org.objectweb.asm.Opcodes.ICONST_2; +import static org.objectweb.asm.Opcodes.ICONST_3; +import static org.objectweb.asm.Opcodes.ICONST_4; +import static org.objectweb.asm.Opcodes.ICONST_5; +import static org.objectweb.asm.Opcodes.ILOAD; +import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.IRETURN; +import static org.objectweb.asm.Opcodes.LLOAD; +import static org.objectweb.asm.Opcodes.LRETURN; +import static org.objectweb.asm.Opcodes.POP; +import static org.objectweb.asm.Opcodes.PUTSTATIC; +import static org.objectweb.asm.Opcodes.RETURN; +import static org.objectweb.asm.Type.getInternalName; + +/** + * Generate subclasses of classes used for transients. All methods delegate to CompositeInvoker. + */ +@SuppressWarnings( "raw" ) +/* package */ final class TransientClassLoader + extends ClassLoader +{ + private static final int JDK_VERSION; + public static final String GENERATED_POSTFIX = "_Proxy"; + + static + { + String jdkString = System.getProperty( "java.specification.version" ); + switch( jdkString ) + { + case "1.8": + JDK_VERSION = Opcodes.V1_8; + break; + case "1.7": + default: + JDK_VERSION = Opcodes.V1_7; + break; + } + } + + /* package */ TransientClassLoader( ClassLoader parent ) + { + super( parent ); + } + + @Override + protected Class findClass( String name ) + throws ClassNotFoundException + { + if( name.endsWith( GENERATED_POSTFIX ) ) + { + Class baseClass; + String baseName = name.substring( 0, name.length() - 6 ); + try + { + baseClass = loadClass( baseName ); + } + catch( ClassNotFoundException e ) + { + // Try replacing the last _ with $ + while( true ) + { + int idx = baseName.lastIndexOf( "_" ); + if( idx != -1 ) + { + baseName = baseName.substring( 0, idx ) + "$" + baseName.substring( idx + 1 ); + try + { + baseClass = loadClass( baseName ); + break; + } + catch( ClassNotFoundException e1 ) + { + // Try again + } + } + else + { + throw e; + } + } + } + + byte[] b = generateClass( name, baseClass ); + return defineClass( name, b, 0, b.length, baseClass.getProtectionDomain() ); + } + + // Try the classloader of this classloader -> get classes in Polygene such as CompositeInvoker + return getClass().getClassLoader().loadClass( name ); + } + + public static byte[] generateClass( String name, Class baseClass ) + throws ClassNotFoundException + { + String classSlash = name.replace( '.', '/' ); + String baseClassSlash = getInternalName( baseClass ); + + ClassWriter cw = new ClassWriter( ClassWriter.COMPUTE_MAXS ); + + // Class definition start + cw.visit( JDK_VERSION, ACC_PUBLIC + ACC_SUPER, classSlash, null, baseClassSlash, new String[] { "org/apache/zest/api/composite/Composite" } ); + + // Composite reference + { + cw.visitField( ACC_PUBLIC, "_instance", "Lorg/apache/polygene/api/composite/CompositeInvoker;", null, null ) + .visitEnd(); + } + + // Static Method references + { + int idx = 1; + for( Method method : baseClass.getMethods() ) + { + if( isOverloaded( method, baseClass ) ) + { + cw.visitField( ACC_PRIVATE + ACC_STATIC, "m" + idx++, "Ljava/lang/reflect/Method;", null, null ) + .visitEnd(); + } + } + } + + // Constructors + for( Constructor constructor : baseClass.getDeclaredConstructors() ) + { + if( Modifier.isPublic( constructor.getModifiers() ) || Modifier.isProtected( constructor.getModifiers() ) ) + { + String desc = org.objectweb.asm.commons.Method.getMethod( constructor ).getDescriptor(); + MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, "<init>", desc, null, null ); + mv.visitCode(); + mv.visitVarInsn( ALOAD, 0 ); + + int idx = 1; + for( Class aClass : constructor.getParameterTypes() ) + { + // TODO Handle other types than objects (?) + mv.visitVarInsn( ALOAD, idx++ ); + } + + mv.visitMethodInsn( INVOKESPECIAL, baseClassSlash, "<init>", desc, false ); + mv.visitInsn( RETURN ); + mv.visitMaxs( idx, idx ); + mv.visitEnd(); + } + } + + // Overloaded and unimplemented methods + Method[] methods = baseClass.getMethods(); + int idx = 0; + List<Label> exceptionLabels = new ArrayList<>(); + for( Method method : methods ) + { + if( isOverloaded( method, baseClass ) ) + { + idx++; + String methodName = method.getName(); + String desc = org.objectweb.asm.commons.Method.getMethod( method ).getDescriptor(); + + String[] exceptions = null; + { + MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, methodName, desc, null, exceptions ); + if( isInternalPolygeneMethod( method, baseClass ) ) + { + // generate a NoOp method... + mv.visitInsn( RETURN ); + } + else + { + Label endLabel = null; // Use this if return type is void + if( method.getExceptionTypes().length > 0 ) + { + exceptions = new String[ method.getExceptionTypes().length ]; + for( int i = 0; i < method.getExceptionTypes().length; i++ ) + { + Class<?> aClass = method.getExceptionTypes()[ i ]; + exceptions[ i ] = getInternalName( aClass ); + } + } + mv.visitCode(); + Label l0 = new Label(); + Label l1 = new Label(); + + exceptionLabels.clear(); + for( Class<?> declaredException : method.getExceptionTypes() ) + { + Label ld = new Label(); + mv.visitTryCatchBlock( l0, l1, ld, getInternalName( declaredException ) ); + exceptionLabels.add( ld ); // Reuse this further down for the catch + } + + Label lruntime = new Label(); + mv.visitTryCatchBlock( l0, l1, lruntime, "java/lang/RuntimeException" ); + Label lerror = new Label(); + mv.visitTryCatchBlock( l0, l1, lerror, "java/lang/Throwable" ); + + mv.visitLabel( l0 ); + mv.visitVarInsn( ALOAD, 0 ); + mv.visitFieldInsn( GETFIELD, classSlash, "_instance", + "Lorg/apache/polygene/api/composite/CompositeInvoker;" ); + mv.visitFieldInsn( GETSTATIC, classSlash, "m" + idx, "Ljava/lang/reflect/Method;" ); + + int paramCount = method.getParameterTypes().length; + int stackIdx = 0; + if( paramCount == 0 ) + { + // Send in null as parameter + mv.visitInsn( ACONST_NULL ); + } + else + { + insn( mv, paramCount ); + mv.visitTypeInsn( ANEWARRAY, "java/lang/Object" ); + int pidx = 0; + for( Class<?> aClass : method.getParameterTypes() ) + { + mv.visitInsn( DUP ); + insn( mv, pidx++ ); + stackIdx = wrapParameter( mv, aClass, stackIdx + 1 ); + mv.visitInsn( AASTORE ); + } + } + + // Call method + mv.visitMethodInsn( INVOKEINTERFACE, "org/apache/zest/api/composite/CompositeInvoker", + "invokeComposite", + "(Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true ); + + // Return value + if( !method.getReturnType().equals( Void.TYPE ) ) + { + unwrapResult( mv, method.getReturnType(), l1 ); + } + else + { + mv.visitInsn( POP ); + mv.visitLabel( l1 ); + endLabel = new Label(); + mv.visitJumpInsn( GOTO, endLabel ); + } + + // Increase stack to beyond method args + stackIdx++; + + // Declared exceptions + int exceptionIdx = 0; + for( Class<?> aClass : method.getExceptionTypes() ) + { + mv.visitLabel( exceptionLabels.get( exceptionIdx++ ) ); + mv.visitFrame( Opcodes.F_SAME1, 0, null, 1, new Object[]{ getInternalName( aClass ) } ); + mv.visitVarInsn( ASTORE, stackIdx ); + mv.visitVarInsn( ALOAD, stackIdx ); + mv.visitInsn( ATHROW ); + } + + // RuntimeException and Error catch-all + mv.visitLabel( lruntime ); + mv.visitFrame( Opcodes.F_SAME1, 0, null, 1, new Object[]{ "java/lang/RuntimeException" } ); + mv.visitVarInsn( ASTORE, stackIdx ); + mv.visitVarInsn( ALOAD, stackIdx ); + mv.visitInsn( ATHROW ); + + mv.visitLabel( lerror ); + mv.visitFrame( Opcodes.F_SAME1, 0, null, 1, new Object[]{ "java/lang/Throwable" } ); + mv.visitVarInsn( ASTORE, stackIdx ); + mv.visitVarInsn( ALOAD, stackIdx ); + mv.visitTypeInsn( CHECKCAST, "java/lang/Error" ); + mv.visitInsn( ATHROW ); + + // Return type = void + if( endLabel != null ) + { + mv.visitLabel( endLabel ); + mv.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + mv.visitInsn( RETURN ); + } + + mv.visitMaxs( 0, 0 ); + mv.visitEnd(); + } + } + + if( !Modifier.isAbstract( method.getModifiers() ) ) + { + // Add method with _ as prefix + MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, "_" + method.getName(), desc, null, exceptions ); + mv.visitCode(); + mv.visitVarInsn( ALOAD, 0 ); + + // Parameters + int stackIdx = 1; + for( Class<?> aClass : method.getParameterTypes() ) + { + stackIdx = loadParameter( mv, aClass, stackIdx ) + 1; + } + + // Call method + mv.visitMethodInsn( INVOKESPECIAL, baseClassSlash, method.getName(), desc, false ); + + // Return value + if( !method.getReturnType().equals( Void.TYPE ) ) + { + returnResult( mv, method.getReturnType() ); + } + else + { + mv.visitInsn( RETURN ); + } + + mv.visitMaxs( 1, 1 ); + mv.visitEnd(); + } + } + } + + // Class initializer + { + MethodVisitor mv = cw.visitMethod( ACC_STATIC, "<clinit>", "()V", null, null ); + mv.visitCode(); + Label l0 = new Label(); + Label l1 = new Label(); + Label l2 = new Label(); + mv.visitTryCatchBlock( l0, l1, l2, "java/lang/NoSuchMethodException" ); + mv.visitLabel( l0 ); + + // Lookup methods and store in static variables + int midx = 0; + for( Method method : methods ) + { + if( isOverloaded( method, baseClass ) ) + { + method.setAccessible( true ); + Class methodClass = method.getDeclaringClass(); + + midx++; + + mv.visitLdcInsn( Type.getType( methodClass ) ); + mv.visitLdcInsn( method.getName() ); + insn( mv, method.getParameterTypes().length ); + mv.visitTypeInsn( ANEWARRAY, "java/lang/Class" ); + + int pidx = 0; + for( Class<?> aClass : method.getParameterTypes() ) + { + mv.visitInsn( DUP ); + insn( mv, pidx++ ); + type( mv, aClass ); + mv.visitInsn( AASTORE ); + } + + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Class", "getMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false ); + mv.visitFieldInsn( PUTSTATIC, classSlash, "m" + midx, "Ljava/lang/reflect/Method;" ); + } + } + + mv.visitLabel( l1 ); + Label l3 = new Label(); + mv.visitJumpInsn( GOTO, l3 ); + mv.visitLabel( l2 ); + mv.visitFrame( Opcodes.F_SAME1, 0, null, 1, new Object[]{ "java/lang/NoSuchMethodException" } ); + mv.visitVarInsn( ASTORE, 0 ); + mv.visitVarInsn( ALOAD, 0 ); + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/NoSuchMethodException", "printStackTrace", "()V", false ); + mv.visitLabel( l3 ); + mv.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + mv.visitInsn( RETURN ); + mv.visitMaxs( 6, 1 ); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } + + private static boolean isOverloaded( Method method, Class baseClass ) + { + return !Modifier.isFinal( method.getModifiers() ); + } + + private static boolean isInternalPolygeneMethod( Method method, Class baseClass ) + { + return isDeclaredIn( method, Initializable.class, baseClass ) + || isDeclaredIn( method, Lifecycle.class, baseClass ); + } + + private static boolean isDeclaredIn( Method method, Class<?> clazz, Class<?> baseClass ) + { + if( !clazz.isAssignableFrom( baseClass ) ) + { + return false; + } + + try + { + clazz.getMethod( method.getName(), method.getParameterTypes() ); + return true; + } + catch( NoSuchMethodException e ) + { + return false; + } + } + + private static Class<?> getInterfaceMethodDeclaration( Method method, Class clazz ) + throws NoSuchMethodException + { + return interfacesOf( clazz ).map( RAW_CLASS ).filter( intFace -> { + try + { + intFace.getMethod( method.getName(), method.getParameterTypes() ); + return true; + } + catch( NoSuchMethodException e ) + { + // Try next + return false; + } + } ).findFirst().orElseThrow( () -> new NoSuchMethodException( method.getName() ) ); + } + + private static boolean isInterfaceMethod( Method method, Class<?> baseClass ) + { + return interfacesOf( baseClass ).map( RAW_CLASS ).filter( Methods.HAS_METHODS ).anyMatch( clazz -> { + try + { + Method m = clazz.getMethod( method.getName(), method.getParameterTypes() ); + m.setAccessible( true ); + return true; + } + catch( NoSuchMethodException e ) + { + // Ignore + } + return false; + } ); + } + + private static void type( MethodVisitor mv, Class<?> aClass ) + { + if( aClass.equals( Integer.TYPE ) ) + { + mv.visitFieldInsn( GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;" ); + } + else if( aClass.equals( Long.TYPE ) ) + { + mv.visitFieldInsn( GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;" ); + } + else if( aClass.equals( Short.TYPE ) ) + { + mv.visitFieldInsn( GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;" ); + } + else if( aClass.equals( Byte.TYPE ) ) + { + mv.visitFieldInsn( GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;" ); + } + else if( aClass.equals( Double.TYPE ) ) + { + mv.visitFieldInsn( GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;" ); + } + else if( aClass.equals( Float.TYPE ) ) + { + mv.visitFieldInsn( GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;" ); + } + else if( aClass.equals( Boolean.TYPE ) ) + { + mv.visitFieldInsn( GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;" ); + } + else if( aClass.equals( Character.TYPE ) ) + { + mv.visitFieldInsn( GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;" ); + } + else + { + mv.visitLdcInsn( Type.getType( aClass ) ); + } + } + + private static int wrapParameter( MethodVisitor mv, Class<?> aClass, int idx ) + { + if( aClass.equals( Integer.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false ); + } + else if( aClass.equals( Long.TYPE ) ) + { + mv.visitVarInsn( LLOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false ); + idx++; // Extra jump + } + else if( aClass.equals( Short.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false ); + } + else if( aClass.equals( Byte.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false ); + } + else if( aClass.equals( Double.TYPE ) ) + { + mv.visitVarInsn( DLOAD, idx ); + idx++; // Extra jump + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false ); + } + else if( aClass.equals( Float.TYPE ) ) + { + mv.visitVarInsn( FLOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false ); + } + else if( aClass.equals( Boolean.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false ); + } + else if( aClass.equals( Character.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false ); + } + else + { + mv.visitVarInsn( ALOAD, idx ); + } + + return idx; + } + + private static void unwrapResult( MethodVisitor mv, Class<?> aClass, Label label ) + { + if( aClass.equals( Integer.TYPE ) ) + { + mv.visitTypeInsn( CHECKCAST, "java/lang/Integer" ); + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false ); + mv.visitLabel( label ); + mv.visitInsn( IRETURN ); + } + else if( aClass.equals( Long.TYPE ) ) + { + mv.visitTypeInsn( CHECKCAST, "java/lang/Long" ); + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false ); + mv.visitLabel( label ); + mv.visitInsn( LRETURN ); + } + else if( aClass.equals( Short.TYPE ) ) + { + mv.visitTypeInsn( CHECKCAST, "java/lang/Short" ); + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false ); + mv.visitLabel( label ); + mv.visitInsn( IRETURN ); + } + else if( aClass.equals( Byte.TYPE ) ) + { + mv.visitTypeInsn( CHECKCAST, "java/lang/Byte" ); + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false ); + mv.visitLabel( label ); + mv.visitInsn( IRETURN ); + } + else if( aClass.equals( Double.TYPE ) ) + { + mv.visitTypeInsn( CHECKCAST, "java/lang/Double" ); + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false ); + mv.visitLabel( label ); + mv.visitInsn( DRETURN ); + } + else if( aClass.equals( Float.TYPE ) ) + { + mv.visitTypeInsn( CHECKCAST, "java/lang/Float" ); + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false ); + mv.visitLabel( label ); + mv.visitInsn( FRETURN ); + } + else if( aClass.equals( Boolean.TYPE ) ) + { + mv.visitTypeInsn( CHECKCAST, "java/lang/Boolean" ); + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false ); + mv.visitLabel( label ); + mv.visitInsn( IRETURN ); + } + else if( aClass.equals( Character.TYPE ) ) + { + mv.visitTypeInsn( CHECKCAST, "java/lang/Character" ); + mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false ); + mv.visitLabel( label ); + mv.visitInsn( IRETURN ); + } + else + { + mv.visitTypeInsn( CHECKCAST, getInternalName( aClass ) ); + mv.visitLabel( label ); + mv.visitInsn( ARETURN ); + } + } + + private static int loadParameter( MethodVisitor mv, Class<?> aClass, int idx ) + { + if( aClass.equals( Integer.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + } + else if( aClass.equals( Long.TYPE ) ) + { + mv.visitVarInsn( LLOAD, idx ); + idx++; // Extra jump + } + else if( aClass.equals( Short.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + } + else if( aClass.equals( Byte.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + } + else if( aClass.equals( Double.TYPE ) ) + { + mv.visitVarInsn( DLOAD, idx ); + idx++; // Extra jump + } + else if( aClass.equals( Float.TYPE ) ) + { + mv.visitVarInsn( FLOAD, idx ); + } + else if( aClass.equals( Boolean.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + } + else if( aClass.equals( Character.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + } + else + { + mv.visitVarInsn( ALOAD, idx ); + } + + return idx; + } + + private static void returnResult( MethodVisitor mv, Class<?> aClass ) + { + if( aClass.equals( Integer.TYPE ) ) + { + mv.visitInsn( IRETURN ); + } + else if( aClass.equals( Long.TYPE ) ) + { + mv.visitInsn( LRETURN ); + } + else if( aClass.equals( Short.TYPE ) ) + { + mv.visitInsn( IRETURN ); + } + else if( aClass.equals( Byte.TYPE ) ) + { + mv.visitInsn( IRETURN ); + } + else if( aClass.equals( Double.TYPE ) ) + { + mv.visitInsn( DRETURN ); + } + else if( aClass.equals( Float.TYPE ) ) + { + mv.visitInsn( FRETURN ); + } + else if( aClass.equals( Boolean.TYPE ) ) + { + mv.visitInsn( IRETURN ); + } + else if( aClass.equals( Character.TYPE ) ) + { + mv.visitInsn( IRETURN ); + } + else + { + mv.visitTypeInsn( CHECKCAST, getInternalName( aClass ) ); + mv.visitInsn( ARETURN ); + } + } + + private static void insn( MethodVisitor mv, int length ) + { + switch( length ) + { + case 0: + mv.visitInsn( ICONST_0 ); + return; + case 1: + mv.visitInsn( ICONST_1 ); + return; + case 2: + mv.visitInsn( ICONST_2 ); + return; + case 3: + mv.visitInsn( ICONST_3 ); + return; + case 4: + mv.visitInsn( ICONST_4 ); + return; + case 5: + mv.visitInsn( ICONST_5 ); + return; + default: + mv.visitIntInsn( BIPUSH, length ); + } + } + + public static boolean isGenerated( Class clazz ) + { + return clazz.getName().endsWith( GENERATED_POSTFIX ); + } + + public static boolean isGenerated( Object object ) + { + return object.getClass().getName().endsWith( GENERATED_POSTFIX ); + } + + public Class loadFragmentClass( Class fragmentClass ) + throws ClassNotFoundException + { + return loadClass( fragmentClass.getName().replace( '$', '_' ) + GENERATED_POSTFIX ); + } + + public static Class getSourceClass( Class fragmentClass ) + { + return fragmentClass.getName().endsWith( GENERATED_POSTFIX ) ? fragmentClass.getSuperclass() : fragmentClass; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientInstance.java new file mode 100644 index 0000000..a251349 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientInstance.java @@ -0,0 +1,224 @@ +/* + * 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.polygene.runtime.composite; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.stream.Stream; +import org.apache.polygene.api.PolygeneAPI; +import org.apache.polygene.api.composite.Composite; +import org.apache.polygene.api.composite.CompositeInstance; +import org.apache.polygene.api.property.StateHolder; +import org.apache.polygene.api.structure.LayerDescriptor; +import org.apache.polygene.api.structure.ModuleDescriptor; + +/** + * InvocationHandler for proxy objects. + */ +public class TransientInstance + implements CompositeInstance, MixinsInstance +{ + public static TransientInstance compositeInstanceOf( Composite composite ) + { + InvocationHandler handler = Proxy.getInvocationHandler( composite ); + return (TransientInstance) handler; + } + + private final Composite proxy; + protected final Object[] mixins; + protected StateHolder state; + protected final CompositeModel compositeModel; + + public TransientInstance( CompositeModel compositeModel, + Object[] mixins, + StateHolder state + ) + { + this.compositeModel = compositeModel; + this.mixins = mixins; + this.state = state; + + proxy = compositeModel.newProxy( this ); + } + + @Override + public Object invoke( Object proxy, Method method, Object[] args ) + throws Throwable + { + return compositeModel.invoke( this, proxy, method, args ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> T proxy() + { + return (T) proxy; + } + + @Override + public <T> T newProxy( Class<T> mixinType ) + throws IllegalArgumentException + { + return compositeModel.newProxy( this, mixinType ); + } + + @Override + public Object invokeComposite( Method method, Object[] args ) + throws Throwable + { + return compositeModel.invoke( this, proxy, method, args ); + } + + @Override + public CompositeModel descriptor() + { + return compositeModel; + } + + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return compositeModel.metaInfo( infoType ); + } + + @Override + public Stream<Class<?>> types() + { + return compositeModel.types(); + } + + @Override + public ModuleDescriptor module() + { + return compositeModel.module(); + } + + public LayerDescriptor layer() + { + return compositeModel.module().layer(); + } + + @Override + public StateHolder state() + { + return state; + } + + @Override + public Object invoke( Object composite, Object[] params, CompositeMethodInstance methodInstance ) + throws Throwable + { + Object mixin = methodInstance.getMixinFrom( mixins ); + return methodInstance.invoke( proxy, params, mixin ); + } + + @Override + public Object invokeObject( Object proxy, Object[] args, Method method ) + throws Throwable + { + return method.invoke( this, args ); + } + + @Override + public boolean equals( Object o ) + { + if( o == null ) + { + return false; + } + if( !Proxy.isProxyClass( o.getClass() ) ) + { + return false; + } + TransientInstance other = (TransientInstance) PolygeneAPI.FUNCTION_COMPOSITE_INSTANCE_OF.apply( (Composite) o ); + if( other.mixins.length != mixins.length ) + { + return false; + } + + for( int i = 0; i < mixins.length; i++ ) + { + if( !mixins[ i ].equals( other.mixins[ i ] ) ) + { + return false; + } + } + return true; + } + + @Override + public int hashCode() + { + int hashCode = 0; + for( Object mixin : mixins ) + { + hashCode = hashCode * 31 + mixin.hashCode(); + } + return hashCode; + } + + @Override + public String toString() + { + StringBuilder buffer = new StringBuilder(); + boolean first = true; + for( Object mixin : mixins ) + { + try + { + if( mixin != null ) // Can happen during construction of incorrect composites, during exception creation. + { + Class<?> type = mixin.getClass(); + Method toStringMethod = type.getMethod( "toString" ); + Class<?> declaringClass = toStringMethod.getDeclaringClass(); + if( !declaringClass.equals( Object.class ) ) + { + if( !first ) + { + buffer.append( ", " ); + } + first = false; + buffer.append( mixin.toString() ); + } + } + } + catch( NoSuchMethodException e ) + { + // Can not happen?? + e.printStackTrace(); + } + } + if( first ) + { + String modelTypeName = compositeModel.getClass().getSimpleName(); + String metaTypeModel = modelTypeName.substring( 0, modelTypeName.length() - 5 ); + return metaTypeModel + "Instance{" + + "mixins=" + Arrays.asList( mixins ) + + ", state=" + state + + ", compositeModel=" + compositeModel + + ", module=" + module() + + '}'; + } + return buffer.toString(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientModel.java new file mode 100644 index 0000000..4aedb47 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientModel.java @@ -0,0 +1,74 @@ +/* + * 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.polygene.runtime.composite; + +import java.util.List; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.composite.TransientDescriptor; +import org.apache.polygene.api.constraint.ConstraintViolationException; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.runtime.injection.InjectionContext; + +/** + * Model for Transient Composites + */ +public class TransientModel extends CompositeModel + implements TransientDescriptor +{ + public TransientModel( ModuleDescriptor module, + List<Class<?>> types, final Visibility visibility, + final MetaInfo metaInfo, + final MixinsModel mixinsModel, + final StateModel stateModel, + final CompositeMethodsModel compositeMethodsModel + ) + { + super( module, types, visibility, metaInfo, mixinsModel, stateModel, compositeMethodsModel ); + } + + public TransientInstance newInstance( UsesInstance uses, + TransientStateInstance state + ) + { + Object[] mixins = mixinsModel.newMixinHolder(); + TransientInstance compositeInstance = new TransientInstance( this, mixins, state ); + + // Instantiate all mixins + int i = 0; + InjectionContext injectionContext = new InjectionContext( compositeInstance, uses, state ); + for( MixinModel mixinModel : mixinsModel.mixinModels() ) + { + mixins[ i++ ] = mixinModel.newInstance( injectionContext ); + } + + // Return + return compositeInstance; + } + + public void checkConstraints( TransientStateInstance instanceState ) + throws ConstraintViolationException + { + stateModel.properties().forEach( propertyModel -> + propertyModel.checkConstraints( instanceState.propertyFor( propertyModel.accessor() ).get() ) + ); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientStateInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientStateInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientStateInstance.java new file mode 100644 index 0000000..c70c3b8 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientStateInstance.java @@ -0,0 +1,62 @@ +/* + * 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.polygene.runtime.composite; + +import java.lang.reflect.AccessibleObject; +import java.util.Map; +import java.util.stream.Stream; +import org.apache.polygene.api.property.Property; +import org.apache.polygene.api.property.StateHolder; + +/** + * TODO + */ +public final class TransientStateInstance + implements StateHolder +{ + private final Map<AccessibleObject, Property<?>> properties; + + public TransientStateInstance( Map<AccessibleObject, Property<?>> properties + ) + { + this.properties = properties; + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> Property<T> propertyFor( AccessibleObject accessor ) + throws IllegalArgumentException + { + Property<T> property = (Property<T>) properties.get( accessor ); + + if( property == null ) + { + throw new IllegalArgumentException( "No such property:" + accessor ); + } + + return property; + } + + @Override + public Stream<Property<?>> properties() + { + return properties.values().stream(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientsModel.java new file mode 100644 index 0000000..a18ef09 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TransientsModel.java @@ -0,0 +1,68 @@ +/* + * 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.polygene.runtime.composite; + +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.composite.TransientDescriptor; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; + +/** + * JAVADOC + */ +public class TransientsModel + implements VisitableHierarchy<Object, Object> +{ + private final List<TransientModel> transientModels; + + public TransientsModel( List<TransientModel> transientModels ) + { + this.transientModels = transientModels; + } + + public Stream<TransientModel> models() + { + return transientModels.stream(); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + for( TransientModel transientModel : transientModels ) + { + if( !transientModel.accept( modelVisitor ) ) + { + break; + } + } + } + return modelVisitor.visitLeave( this ); + } + + public Stream<? extends TransientDescriptor> stream() + { + return transientModels.stream(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TypedModifierInvocationHandler.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TypedModifierInvocationHandler.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TypedModifierInvocationHandler.java new file mode 100644 index 0000000..399e3f2 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/TypedModifierInvocationHandler.java @@ -0,0 +1,53 @@ +/* + * 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.polygene.runtime.composite; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import org.apache.polygene.api.composite.InvalidCompositeException; + +/** + * JAVADOC + */ +public final class TypedModifierInvocationHandler + extends FragmentInvocationHandler +{ + @Override + public Object invoke( Object proxy, Method method, Object[] args ) + throws Throwable + { + try + { + return this.method.invoke( fragment, args ); + } + catch( InvocationTargetException e ) + { + throw cleanStackTrace( e.getTargetException(), proxy, method ); + } + catch( Throwable e ) + { + if( fragment == null ) + { + throw new InvalidCompositeException( "No fragment available for method " + method.getName() ); + } + throw cleanStackTrace( e, proxy, method ); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UnsynchronizedCompositeMethodInstancePool.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UnsynchronizedCompositeMethodInstancePool.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UnsynchronizedCompositeMethodInstancePool.java new file mode 100644 index 0000000..56bfce5 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UnsynchronizedCompositeMethodInstancePool.java @@ -0,0 +1,50 @@ +/* + * 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.polygene.runtime.composite; + +/** + * Method instance pool that keeps a linked list. Uses synchronization + * to ensure that instances are acquired and returned in a thread-safe + * manner. + */ +public final class UnsynchronizedCompositeMethodInstancePool + implements InstancePool<CompositeMethodInstance> +{ + private CompositeMethodInstance first = null; + + @Override + public CompositeMethodInstance obtainInstance() + { + CompositeMethodInstance instance = first; + if( instance != null ) + { + first = instance.getNext(); + } + return instance; + } + + @Override + public void releaseInstance( CompositeMethodInstance instance ) + { + instance.setNext( first ); + first = instance; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UsageGraph.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UsageGraph.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UsageGraph.java new file mode 100644 index 0000000..c0a0b35 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UsageGraph.java @@ -0,0 +1,134 @@ +/* + * 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.polygene.runtime.composite; + +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import org.apache.polygene.bootstrap.BindingException; + +/** + * This class is NOT thread-safe. + * //TODO: Algorithm need to be optimized. + */ +public final class UsageGraph<K> +{ + private final Collection<K> data; + private final Use<K> use; + private final boolean allowCyclic; + private List<K> resolved; + private HashMap<K, List<K>> transitive; + + public UsageGraph( Collection<K> data, Use<K> use, boolean allowCyclic ) + { + this.data = data; + this.use = use; + this.allowCyclic = allowCyclic; + } + + public boolean transitiveUse( K source, K other ) + throws BindingException + { + if( transitive == null ) + { + buildUsageGraph(); + } + return transitive.containsKey( source ) && transitive.get( source ).contains( other ); + } + + private void checkCyclic( List<K> visited, K sourceItem, K used ) + throws BindingException + { + Collection<K> nextLevel = use.uses( used ); + for( K next : nextLevel ) + { + if( next == sourceItem ) + { + if( !allowCyclic ) + { + visited.add( next ); + throw new BindingException( "Cyclic usage detected: " + sourceItem + " -> " + visited ); + } + } + if( !visited.contains( next ) ) + { + visited.add( next ); + checkCyclic( visited, sourceItem, next ); + } + } + } + + /** + * Must be called if the data set has been modified. + */ + public void invalidate() + { + resolved = null; + transitive = null; + } + + public List<K> resolveOrder() + throws BindingException + { + if( resolved == null ) + { + buildUsageGraph(); + resolved = new LinkedList<K>(); + for( K item : data ) + { + int pos = resolved.size(); + for( K entry : resolved ) + { + if( transitiveUse( entry, item ) ) + { + pos = resolved.indexOf( entry ); + break; + } + } + resolved.add( pos, item ); + } + } + return resolved; + } + + private void buildUsageGraph() + throws BindingException + { + transitive = new HashMap<K, List<K>>(); + for( K sourceItem : data ) + { + LinkedList<K> visited = new LinkedList<K>(); + checkCyclic( visited, sourceItem, sourceItem ); + transitive.put( sourceItem, visited ); + } + } + + public interface Use<K> + { + + /** + * @param source The item to be queried. + * + * @return A list of items it uses. + */ + Collection<K> uses( K source ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UsesInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UsesInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UsesInstance.java new file mode 100644 index 0000000..0fc8eb3 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/UsesInstance.java @@ -0,0 +1,112 @@ +/* + * 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.polygene.runtime.composite; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * JAVADOC + */ +public final class UsesInstance +{ + public static final UsesInstance EMPTY_USES; + private final Set<Object> uses; + + static + { + EMPTY_USES = new UsesInstance( new HashSet<>() ); + } + + private UsesInstance( HashSet<Object> uses ) + { + this.uses = Collections.unmodifiableSet( uses ); + } + + public UsesInstance use( Object... objects ) + { + HashSet<Object> useObjects = new HashSet<>(); + if( !uses.isEmpty() ) + { + useObjects.addAll( uses ); + for( Object object : objects ) + { + Object oldUseForType = useForType( object.getClass() ); + if( oldUseForType != null ) + { + useObjects.remove( oldUseForType ); + } + } + } + useObjects.addAll( Arrays.asList( objects ) ); + return new UsesInstance( useObjects ); + } + + public Object useForType( Class<?> type ) + { + // Check instances first + for( Object use : uses ) + { + if( type.isInstance( use ) ) + { + return use; + } + } + + return null; + } + + public Object[] toArray() + { + return uses.toArray(); + } + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + UsesInstance that = (UsesInstance) o; + return uses.equals( that.uses ); + } + + @Override + public int hashCode() + { + return uses.hashCode(); + } + + @Override + public String toString() + { + return "UsesInstance{" + + "uses=" + uses + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ValueConstraintsInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ValueConstraintsInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ValueConstraintsInstance.java new file mode 100644 index 0000000..783a4ef --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ValueConstraintsInstance.java @@ -0,0 +1,145 @@ +/* + * 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.polygene.runtime.composite; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.common.Optional; +import org.apache.polygene.api.constraint.ConstraintViolation; +import org.apache.polygene.api.constraint.ConstraintViolationException; + +/** + * JAVADOC + */ +public final class ValueConstraintsInstance +{ + private static final Optional OPTIONAL; + + static + { + OPTIONAL = new OptionalDummy(); + } + + @SuppressWarnings( "raw" ) + private final List<ConstraintInstance> constraints; + private String name; + private boolean optional; + + public ValueConstraintsInstance( List<AbstractConstraintModel> constraintModels, String name, boolean optional ) + { + this.name = name; + this.optional = optional; + constraints = new ArrayList<>(); + for( AbstractConstraintModel constraintModel : constraintModels ) + { + constraints.add( constraintModel.newInstance() ); + } + } + + @SuppressWarnings( { "raw", "unchecked" } ) + public List<ConstraintViolation> checkConstraints( Object value ) + { + List<ConstraintViolation> violations = null; + + // Check optional first - this avoids NPE's in constraints + if( optional ) + { + if( value == null ) + { + violations = Collections.emptyList(); + } + } + else + { + if( value == null ) + { + violations = new ArrayList<>(); + violations.add( new ConstraintViolation( name, OPTIONAL, null ) ); + } + } + + if( violations == null && value != null ) + { + for( ConstraintInstance constraint : constraints ) + { + boolean valid; + try + { + valid = constraint.isValid( value ); + } + catch( NullPointerException e ) + { + // A NPE is the same as a failing constraint + valid = false; + } + + if( !valid ) + { + if( violations == null ) + { + violations = new ArrayList<>(); + } + ConstraintViolation violation = new ConstraintViolation( name, constraint.annotation(), value ); + violations.add( violation ); + } + } + } + + if( violations == null ) + { + violations = Collections.emptyList(); + } + + return violations; + } + + public void checkConstraints( Object value, AccessibleObject accessor ) + { + List<ConstraintViolation> violations = checkConstraints( value ); + if( !violations.isEmpty() ) + { + Stream<Class<?>> empty = Stream.empty(); + throw new ConstraintViolationException( "", empty, (Member) accessor, violations ); + } + } + + @SuppressWarnings( "AnnotationAsSuperInterface" ) + private static class OptionalDummy + implements Optional + { + @Override + public Class<? extends Annotation> annotationType() + { + return Optional.class; + } + + @Override + public String toString() + { + return "not optional"; + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ValueConstraintsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ValueConstraintsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ValueConstraintsModel.java new file mode 100644 index 0000000..5568881 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/ValueConstraintsModel.java @@ -0,0 +1,72 @@ +/* + * 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.polygene.runtime.composite; + +import java.util.List; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; + +/** + * JAVADOC + */ +public final class ValueConstraintsModel + implements VisitableHierarchy<Object, Object> +{ + private final List<AbstractConstraintModel> constraintModels; + private String name; + private boolean optional; + + public ValueConstraintsModel( List<AbstractConstraintModel> constraintModels, String name, boolean optional ) + { + this.constraintModels = constraintModels; + this.name = name; + this.optional = optional; + } + + public ValueConstraintsInstance newInstance() + { + return new ValueConstraintsInstance( constraintModels, name, optional ); + } + + public boolean isConstrained() + { + if( !constraintModels.isEmpty() ) + { + return true; + } + + return !optional; + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + for( AbstractConstraintModel constraintModel : constraintModels ) + { + if( constraintModel.accept( modelVisitor ) ) + { + return false; + } + } + return true; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntitiesModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntitiesModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntitiesModel.java new file mode 100644 index 0000000..ebb3d78 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntitiesModel.java @@ -0,0 +1,68 @@ +/* + * 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.polygene.runtime.entity; + +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.entity.EntityDescriptor; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; + +/** + * Model of entities in a particular Module. + */ +public class EntitiesModel + implements VisitableHierarchy<Object, Object> +{ + private final List<EntityModel> entityModels; + + public EntitiesModel( List<EntityModel> entityModels ) + { + this.entityModels = entityModels; + } + + public Stream<EntityModel> models() + { + return entityModels.stream(); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + for( EntityModel entityModel : entityModels ) + { + if( !entityModel.accept( modelVisitor ) ) + { + break; + } + } + } + return modelVisitor.visitLeave( this ); + } + + public Stream<? extends EntityDescriptor> stream() + { + return entityModels.stream(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityInstance.java new file mode 100644 index 0000000..a58cee9 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/entity/EntityInstance.java @@ -0,0 +1,317 @@ +/* + * 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.polygene.runtime.entity; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.AssociationStateDescriptor; +import org.apache.polygene.api.composite.CompositeDescriptor; +import org.apache.polygene.api.composite.CompositeInstance; +import org.apache.polygene.api.constraint.ConstraintViolationException; +import org.apache.polygene.api.entity.EntityComposite; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.identity.HasIdentity; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.unitofwork.NoSuchEntityException; +import org.apache.polygene.api.unitofwork.UnitOfWork; +import org.apache.polygene.api.unitofwork.UnitOfWorkException; +import org.apache.polygene.runtime.composite.CompositeMethodInstance; +import org.apache.polygene.runtime.composite.MixinsInstance; +import org.apache.polygene.spi.entity.EntityState; +import org.apache.polygene.spi.entity.EntityStatus; + +import static java.util.stream.Collectors.toList; + +/** + * Entity instance + */ +public final class EntityInstance + implements CompositeInstance, MixinsInstance +{ + public static EntityInstance entityInstanceOf( EntityComposite composite ) + { + return (EntityInstance) Proxy.getInvocationHandler( composite ); + } + + private final EntityComposite proxy; + private final UnitOfWork uow; + private final EntityModel entityModel; + private final EntityReference reference; + private final EntityState entityState; + + private Object[] mixins; + private EntityStateInstance state; + + public EntityInstance( UnitOfWork uow, + EntityModel entityModel, + EntityState entityState + ) + { + this.uow = uow; + this.entityModel = entityModel; + this.reference = entityState.entityReference(); + this.entityState = entityState; + + proxy = (EntityComposite) entityModel.newProxy( this ); + } + + @Override + public Object invoke( Object proxy, Method method, Object[] args ) + throws Throwable + { + return entityModel.invoke( this, this.proxy, method, args ); + } + + public EntityReference reference() + { + return reference; + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> T proxy() + { + return (T) proxy; + } + + @Override + public CompositeDescriptor descriptor() + { + return entityModel; + } + + @Override + public <T> T newProxy( Class<T> mixinType ) + throws IllegalArgumentException + { + return entityModel.newProxy( this, mixinType ); + } + + @Override + public Object invokeComposite( Method method, Object[] args ) + throws Throwable + { + return entityModel.invoke( this, proxy, method, args ); + } + + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return entityModel.metaInfo( infoType ); + } + + public EntityModel entityModel() + { + return entityModel; + } + + @Override + public Stream<Class<?>> types() + { + return entityModel.types(); + } + + @Override + public ModuleDescriptor module() + { + return entityModel.module(); + } + + public UnitOfWork unitOfWork() + { + return uow; + } + + public EntityState entityState() + { + return entityState; + } + + @Override + public EntityStateInstance state() + { + if( state == null ) + { + initState(); + } + + return state; + } + + public EntityStatus status() + { + return entityState.status(); + } + + @Override + public Object invoke( Object composite, Object[] params, CompositeMethodInstance methodInstance ) + throws Throwable + { + if( mixins == null ) + { + initState(); + } + + Object mixin = methodInstance.getMixinFrom( mixins ); + + if( mixin == null ) + { + mixin = entityModel.newMixin( mixins, state, this, methodInstance.method() ); + } + + return methodInstance.invoke( proxy, params, mixin ); + } + + @Override + public Object invokeObject( Object proxy, Object[] args, Method method ) + throws Throwable + { + return method.invoke( this, args ); + } + + private void initState() + { + if( !uow.isOpen() ) + { + throw new UnitOfWorkException( "Unit of work has been closed" ); + } + + if( status() == EntityStatus.REMOVED ) + { + throw new NoSuchEntityException(reference, entityModel.types(), unitOfWork().usecase() ); + } + + mixins = entityModel.newMixinHolder(); + state = new EntityStateInstance( entityModel.state(), uow, entityState ); + } + + @Override + public int hashCode() + { + return reference.hashCode(); + } + + @Override + public boolean equals( Object o ) + { + try + { + HasIdentity other = ( (HasIdentity) o ); + return other != null && other.identity().get().equals( reference.identity() ); + } + catch( ClassCastException e ) + { + return false; + } + } + + @Override + public String toString() + { + if( Boolean.getBoolean( "polygene.entity.print.state" ) ) + { + return state.toString(); + } + else + { + return reference.toString(); + } + } + + public void remove( UnitOfWork unitOfWork ) + { + invokeRemove(); + + removeAggregatedEntities( unitOfWork ); + + entityState.remove(); + mixins = null; + } + + public void invokeCreate() + { + lifecyleInvoke( true ); + } + + private void invokeRemove() + { + lifecyleInvoke( false ); + } + + private void lifecyleInvoke( boolean create ) + { + if( mixins == null ) + { + initState(); + } + + entityModel.invokeLifecycle( create, mixins, this, state ); + } + + private void removeAggregatedEntities( UnitOfWork unitOfWork ) + { + // Calculate aggregated Entities + AssociationStateDescriptor stateDescriptor = entityModel.state(); + Stream.concat( + stateDescriptor.associations() + .filter( AssociationDescriptor::isAggregated ) + .map( association -> state.associationFor( association.accessor() ).get() ) + .filter( Objects::nonNull ), + + Stream.concat( + stateDescriptor.manyAssociations() + .filter( AssociationDescriptor::isAggregated ) + .flatMap( association -> state.manyAssociationFor( association.accessor() ).toList().stream() ) + .filter( Objects::nonNull ), + + stateDescriptor.namedAssociations() + .filter( AssociationDescriptor::isAggregated ) + .flatMap( association -> state.namedAssociationFor( association.accessor() ) + .toMap() + .values() + .stream() ) + .filter( Objects::nonNull ) + ) + ).distinct().collect( Collectors.toList() ).forEach( unitOfWork::remove ); + } + + public void checkConstraints() + { + try + { + state.checkConstraints(); + } + catch( ConstraintViolationException e ) + { + List<? extends Type> entityModelList = entityModel.types().collect( toList() ); + throw new ConstraintViolationException( reference.identity(), + entityModelList, + e.mixinTypeName(), + e.methodName(), + e.constraintViolations() ); + } + } +}
