Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id D8EB8200D70 for ; Thu, 21 Dec 2017 08:12:04 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id D7427160C2E; Thu, 21 Dec 2017 07:12:04 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id D0CCE160C1A for ; Thu, 21 Dec 2017 08:12:01 +0100 (CET) Received: (qmail 11150 invoked by uid 500); 21 Dec 2017 07:12:00 -0000 Mailing-List: contact commits-help@polygene.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@polygene.apache.org Delivered-To: mailing list commits@polygene.apache.org Received: (qmail 10231 invoked by uid 99); 21 Dec 2017 07:11:57 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 21 Dec 2017 07:11:57 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 08B17F21A2; Thu, 21 Dec 2017 07:11:55 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: niclas@apache.org To: commits@polygene.apache.org Date: Thu, 21 Dec 2017 07:12:25 -0000 Message-Id: In-Reply-To: <309c5146cae346f09aa06a2a98a9f065@git.apache.org> References: <309c5146cae346f09aa06a2a98a9f065@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [32/57] [abbrv] [partial] polygene-java git commit: Revert "First round of changes to move to org.apache.zest namespace." archived-at: Thu, 21 Dec 2017 07:12:05 -0000 http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/util/Base64Encoder.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/util/Base64Encoder.java b/core/api/src/main/java/org/qi4j/api/util/Base64Encoder.java new file mode 100644 index 0000000..9246b13 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/util/Base64Encoder.java @@ -0,0 +1,224 @@ +/* + * Copyright 2009 Alin Dreghiciu. + * + * Licensed 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.qi4j.api.util; + +/** + * Base64Encoder. + */ +public final class Base64Encoder +{ + + /** + * Utility class. ment to be used via static methods. + */ + private Base64Encoder() + { + // utility class + } + + /** + * Encodes a String into a base 64 String. The resulting encoding is chunked at 76 bytes. + * + * @param s String to encode. + * + * @return encoded string. + */ + public static String encode( String s, boolean includePadding ) + { + byte[] sBytes = s.getBytes(); + sBytes = encode( sBytes, includePadding ); + s = new String( sBytes ); + return s; + } + + /** + * Decodes a base 64 String into a String. + * + * @param s String to decode. + * + * @return encoded string. + * + * @throws java.lang.IllegalArgumentException + * _ If the given byte array was not valid base64 encoding. + */ + public static String decode( String s ) + throws IllegalArgumentException + { + s = s.replaceAll( "\n", "" ); + s = s.replaceAll( "\r", "" ); + byte[] sBytes = s.getBytes(); + sBytes = decode( sBytes ); + s = new String( sBytes ); + return s; + } + + private static final byte[] ALPHASET = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".getBytes(); + + private static final int I6O2 = 255 - 3; + private static final int O6I2 = 3; + private static final int I4O4 = 255 - 15; + private static final int O4I4 = 15; + private static final int I2O6 = 255 - 63; + private static final int O2I6 = 63; + + /** + * Encodes a byte array into a base 64 byte array. + * + * @param dData byte array to encode. + * + * @return encoded byte array. + */ + public static byte[] encode( byte[] dData, boolean includePadding ) + { + if( dData == null ) + { + throw new IllegalArgumentException( "Cannot encode null" ); + } + byte[] eData = new byte[ ( ( dData.length + 2 ) / 3 ) * 4 ]; + + int eIndex = 0; + for( int i = 0; i < dData.length; i += 3 ) + { + int d1; + int d2 = 0; + int d3 = 0; + int e1; + int e2; + int e3; + int e4; + int pad = 0; + + d1 = dData[ i ]; + if( ( i + 1 ) < dData.length ) + { + d2 = dData[ i + 1 ]; + if( ( i + 2 ) < dData.length ) + { + d3 = dData[ i + 2 ]; + } + else + { + pad = 1; + } + } + else + { + pad = 2; + } + + e1 = ALPHASET[ ( d1 & I6O2 ) >> 2 ]; + e2 = ALPHASET[ ( d1 & O6I2 ) << 4 | ( d2 & I4O4 ) >> 4 ]; + e3 = ALPHASET[ ( d2 & O4I4 ) << 2 | ( d3 & I2O6 ) >> 6 ]; + e4 = ALPHASET[ ( d3 & O2I6 ) ]; + + eData[ eIndex++ ] = (byte) e1; + eData[ eIndex++ ] = (byte) e2; + eData[ eIndex++ ] = ( pad < 2 ) ? (byte) e3 : (byte) '='; + eData[ eIndex++ ] = ( pad < 1 ) ? (byte) e4 : (byte) '='; + + if( pad > 0 && !includePadding ) + { + byte[] neweData = new byte[ eData.length - pad ]; + System.arraycopy( eData, 0, neweData, 0, eIndex - pad ); + eData = neweData; + } + } + + return eData; + } + + private final static int[] CODES = new int[ 256 ]; + + static + { + for( int i = 0; i < CODES.length; i++ ) + { + CODES[ i ] = 64; + } + for( int i = 0; i < ALPHASET.length; i++ ) + { + CODES[ ALPHASET[ i ] ] = i; + } + } + + /** + * Decodes a base64 byte array into a byte array. + *

+ * + * @param eData byte array to decode. + * + * @return decoded byte array. + * + * @throws java.lang.IllegalArgumentException + * thrown if the given byte array was not valid com.sun.syndication.io.impl.Base64 encoding. + */ + public static byte[] decode( byte[] eData ) + { + if( eData == null ) + { + throw new IllegalArgumentException( "Cannot decode null" ); + } + byte[] cleanEData = eData.clone(); + int cleanELength = 0; + for( byte anEData : eData ) + { + if( anEData < 256 && CODES[ anEData ] < 64 ) + { + cleanEData[ cleanELength++ ] = anEData; + } + } + + int dLength = ( cleanELength / 4 ) * 3; + switch( cleanELength % 4 ) + { + case 3: + dLength += 2; + break; + case 2: + dLength++; + break; + } + + byte[] dData = new byte[ dLength ]; + int dIndex = 0; + for( int i = 0; i < eData.length; i += 4 ) + { + if( ( i + 3 ) > eData.length ) + { + throw new IllegalArgumentException( + "byte array is not a valid base64 encoding" + ); + } + int e1 = CODES[ cleanEData[ i ] ]; + int e2 = CODES[ cleanEData[ i + 1 ] ]; + int e3 = CODES[ cleanEData[ i + 2 ] ]; + int e4 = CODES[ cleanEData[ i + 3 ] ]; + dData[ dIndex++ ] = (byte) ( ( e1 << 2 ) | ( e2 >> 4 ) ); + if( dIndex < dData.length ) + { + dData[ dIndex++ ] = (byte) ( ( e2 << 4 ) | ( e3 >> 2 ) ); + } + if( dIndex < dData.length ) + { + dData[ dIndex++ ] = (byte) ( ( e3 << 6 ) | ( e4 ) ); + } + } + return dData; + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/util/Classes.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/util/Classes.java b/core/api/src/main/java/org/qi4j/api/util/Classes.java new file mode 100644 index 0000000..34f4b05 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/util/Classes.java @@ -0,0 +1,699 @@ +/* + * Copyright (c) 2008, Rickard Öberg. All Rights Reserved. + * + * Licensed 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.qi4j.api.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.qi4j.api.composite.ModelDescriptor; +import org.qi4j.functional.Function; +import org.qi4j.functional.Iterables; +import org.qi4j.functional.Specification; + +import static org.qi4j.functional.Iterables.cast; +import static org.qi4j.functional.Iterables.empty; +import static org.qi4j.functional.Iterables.flatten; +import static org.qi4j.functional.Iterables.flattenIterables; +import static org.qi4j.functional.Iterables.iterable; +import static org.qi4j.functional.Iterables.map; +import static org.qi4j.functional.Iterables.matchesAny; +import static org.qi4j.functional.Iterables.prepend; + +/** + * Useful methods for handling Classes. + */ +public final class Classes +{ + private final static Map wrapperClasses = new HashMap<>(); + + static + { + wrapperClasses.put( boolean.class, Boolean.class ); + wrapperClasses.put( byte.class, Byte.class ); + wrapperClasses.put( short.class, Short.class ); + wrapperClasses.put( char.class, Character.class ); + wrapperClasses.put( int.class, Integer.class ); + wrapperClasses.put( long.class, Long.class ); + wrapperClasses.put( float.class, Float.class ); + wrapperClasses.put( double.class, Double.class ); + } + + private final static Map primitiveClasses = new HashMap<>(); + + static + { + primitiveClasses.put( boolean.class, Boolean.class ); + primitiveClasses.put( byte.class, Byte.class ); + primitiveClasses.put( short.class, Short.class ); + primitiveClasses.put( char.class, Character.class ); + primitiveClasses.put( int.class, Integer.class ); + primitiveClasses.put( long.class, Long.class ); + primitiveClasses.put( float.class, Float.class ); + primitiveClasses.put( double.class, Double.class ); + } + + /** + * Convert from primitive class (int, short, double, etc.) to wrapper class (Integer, Short, Double, etc.). + * Return the same class if it's not a primitive class. This can therefore safely be used on all types + * to ensure that they are not primitives. + */ + private static final Function WRAPPER_CLASS = new Function() + { + @Override + public Type map( Type aClass ) + { + Type wrapperClass = wrapperClasses.get( aClass ); + return wrapperClass == null ? aClass : wrapperClass; + } + }; + + /** + * Convert from wrapper class (Integer, Short, Double, etc.) to primitive class (int, short, double, etc.). + * Return the same class if it's not a wrapper class. This can therefore safely be used on all types + * to ensure that they are primitives if possible. + */ + @SuppressWarnings( "UnusedDeclaration" ) + private static final Function PRIMITIVE_CLASS = new Function() + { + @Override + public Type map( Type aClass ) + { + Type primitiveClass = primitiveClasses.get( aClass ); + return primitiveClass == null ? aClass : primitiveClass; + } + }; + + /** + * Function that extract the raw class of a type. + */ + public static final Function> RAW_CLASS = new Function>() + { + @Override + public Class map( Type genericType ) + { + // Calculate raw type + if( genericType instanceof Class ) + { + return (Class) genericType; + } + else if( genericType instanceof ParameterizedType ) + { + return (Class) ( (ParameterizedType) genericType ).getRawType(); + } + else if( genericType instanceof TypeVariable ) + { + return (Class) ( (TypeVariable) genericType ).getGenericDeclaration(); + } + else if( genericType instanceof WildcardType ) + { + return (Class) ( (WildcardType) genericType ).getUpperBounds()[ 0]; + } + else if( genericType instanceof GenericArrayType ) + { + Object temp = Array.newInstance( (Class) ( (GenericArrayType) genericType ).getGenericComponentType(), 0 ); + return temp.getClass(); + } + throw new IllegalArgumentException( "Could not extract the raw class of " + genericType ); + } + }; + + private static final Function TYPE_OF = new Function() + { + @Override + public Type map( AccessibleObject accessor ) + { + return accessor instanceof Method ? ( (Method) accessor ).getGenericReturnType() : ( (Field) accessor ).getGenericType(); + } + }; + + private static final Function>> CLASS_HIERARCHY = new Function>>() + { + @Override + @SuppressWarnings( {"raw", "unchecked"} ) + public Iterable> map( Type type ) + { + if( type == null ) + { + return empty(); + } + if( type.equals( Object.class ) ) + { + Class aClass = (Class) type; + return cast( iterable( aClass ) ); + } + else + { + type = RAW_CLASS.map( type ); + Class superclass = ( (Class) type ).getSuperclass(); + return prepend( (Class) type, map( superclass ) ); + } + } + }; + + @SuppressWarnings( "raw" ) + private static final Function> INTERFACES_OF = new Function>() + { + @Override + public Iterable map( Type type ) + { + Class clazz = RAW_CLASS.map( type ); + + if( clazz.isInterface() ) + { + Iterable genericInterfaces = iterable( clazz.getGenericInterfaces() ); + Iterable flattenIterables = flattenIterables( Iterables.map( INTERFACES_OF, genericInterfaces ) ); + return prepend( type, flattenIterables ); + } + else + { + if( type.equals( Object.class ) ) + { + return iterable( clazz.getGenericInterfaces() ); + } + else + { + return flatten( flattenIterables( Iterables.map( INTERFACES_OF, + iterable( clazz.getGenericInterfaces() ) ) ), + INTERFACES_OF.map( RAW_CLASS.map( type ).getSuperclass() ) ); + } + } + } + }; + + @SuppressWarnings( "raw" ) + private static final Function> TYPES_OF = new Function>() + { + @Override + public Iterable map( Type type ) + { + Class clazz = RAW_CLASS.map( type ); + + if( clazz.isInterface() ) + { + Iterable genericInterfaces = iterable( clazz.getGenericInterfaces() ); + Iterable flattenIterables = flattenIterables( Iterables.map( INTERFACES_OF, genericInterfaces ) ); + return prepend( clazz, flattenIterables ); + } + else + { + return flatten( CLASS_HIERARCHY.map( type ), + flattenIterables( Iterables.map( INTERFACES_OF, CLASS_HIERARCHY.map( type ) ) ) ); + } + } + }; + + public static Type typeOf( AccessibleObject from ) + { + return TYPE_OF.map( from ); + } + + public static Iterable typesOf( Iterable types ) + { + Iterable result = empty(); + for( Type type : types ) + { + result = flatten( result, typesOf( type ) ); + } + return result; + } + + public static Iterable typesOf( Type type ) + { + return TYPES_OF.map( type ); + } + + public static Iterable interfacesOf( Iterable types ) + { + Iterable result = empty(); + for( Type type : types ) + { + result = flatten( result, interfacesOf( type ) ); + } + return result; + } + + public static Iterable interfacesOf( Type type ) + { + return INTERFACES_OF.map( type ); + } + + public static Iterable> classHierarchy( Class type ) + { + return CLASS_HIERARCHY.map( type ); + } + + public static Type wrapperClass( Type type ) + { + return WRAPPER_CLASS.map( type ); + } + + public static Specification> isAssignableFrom( final Class clazz ) + { + return new Specification>() + { + @Override + @SuppressWarnings( "unchecked" ) + public boolean satisfiedBy( Class item ) + { + return clazz.isAssignableFrom( item ); + } + }; + } + + @SuppressWarnings( "raw" ) + public static Specification instanceOf( final Class clazz ) + { + return new Specification() + { + @Override + public boolean satisfiedBy( Object item ) + { + return clazz.isInstance( item ); + } + }; + } + + public static Specification> hasModifier( final int classModifier ) + { + return new Specification>() + { + @Override + public boolean satisfiedBy( Class item ) + { + return ( item.getModifiers() & classModifier ) != 0; + } + }; + } + + public static Function> forClassHierarchy( final Function, Iterable> function ) + { + return new Function>() + { + @Override + public Iterable map( Type type ) + { + return flattenIterables( Iterables.map( function, CLASS_HIERARCHY.map( type ) ) ); + } + }; + } + + public static Function> forTypes( final Function> function ) + { + return new Function>() + { + @Override + public Iterable map( Type type ) + { + return flattenIterables( Iterables.map( function, TYPES_OF.map( type ) ) ); + } + }; + } + + @SuppressWarnings( "raw" ) + public static Set> interfacesWithMethods( Set> interfaces ) + { + Set> newSet = new LinkedHashSet<>(); + for( Class type : interfaces ) + { + if( type.isInterface() && type.getDeclaredMethods().length > 0 ) + { + newSet.add( type ); + } + } + + return newSet; + } + + public static String simpleGenericNameOf( Type type ) + { + StringBuilder sb = new StringBuilder(); + simpleGenericNameOf( sb, type ); + return sb.toString(); + } + + @SuppressWarnings( "raw" ) + private static void simpleGenericNameOf( StringBuilder sb, Type type ) + { + if( type instanceof Class ) + { + sb.append( ( (Class) type ).getSimpleName() ); + } + else if( type instanceof ParameterizedType ) + { + ParameterizedType pt = (ParameterizedType) type; + simpleGenericNameOf( sb, pt.getRawType() ); + sb.append( "<" ); + boolean atLeastOne = false; + for( Type typeArgument : pt.getActualTypeArguments() ) + { + if( atLeastOne ) + { + sb.append( ", " ); + } + simpleGenericNameOf( sb, typeArgument ); + atLeastOne = true; + } + sb.append( ">" ); + } + else if( type instanceof GenericArrayType ) + { + GenericArrayType gat = (GenericArrayType) type; + simpleGenericNameOf( sb, gat.getGenericComponentType() ); + sb.append( "[]" ); + } + else if( type instanceof TypeVariable ) + { + TypeVariable tv = (TypeVariable) type; + sb.append( tv.getName() ); + } + else if( type instanceof WildcardType ) + { + WildcardType wt = (WildcardType) type; + sb.append( "? extends " ); + boolean atLeastOne = false; + for( Type typeArgument : wt.getUpperBounds() ) + { + if( atLeastOne ) + { + sb.append( ", " ); + } + simpleGenericNameOf( sb, typeArgument ); + atLeastOne = true; + } + } + else + { + throw new IllegalArgumentException( "Don't know how to deal with type:" + type ); + } + } + + @SuppressWarnings( "UnusedDeclaration" ) + public static + AnnotationType findAnnotationOfTypeOrAnyOfSuperTypes( Class type, Class annotationClass ) + { + AnnotationType result = null; + for( Type clazz : Classes.TYPES_OF.map( type ) ) + { + result = Classes.RAW_CLASS.map( clazz ).getAnnotation( annotationClass ); + if( result != null ) + { + break; + } + } + + return result; + } + + public static Specification memberNamed( final String name ) + { + return new Specification() + { + @Override + public boolean satisfiedBy( Member item ) + { + return item.getName().equals( name ); + } + }; + } + + /** + * Given a type variable, find what it resolves to given the declaring class where type + * variable was found and a top class that extends the declaring class. + * + * @param name The TypeVariable name. + * @param declaringClass The class where the TypeVariable is declared. + * @param topClass The top class that extends the declaringClass + * + * @return The Type instance of the given TypeVariable + */ + @SuppressWarnings( "raw" ) + public static Type resolveTypeVariable( TypeVariable name, Class declaringClass, Class topClass ) + { + Type type = resolveTypeVariable( name, declaringClass, new HashMap(), topClass ); + if( type == null ) + { + type = Object.class; + } + return type; + } + + @SuppressWarnings( "raw" ) + private static Type resolveTypeVariable( TypeVariable name, + Class declaringClass, + Map mappings, + Class current + ) + { + if( current.equals( declaringClass ) ) + { + Type resolvedType = name; + while( resolvedType instanceof TypeVariable ) + { + resolvedType = mappings.get( resolvedType ); + } + return resolvedType; + } + + List types = new ArrayList<>(); + for( Type type : current.getGenericInterfaces() ) + { + Iterable interfaces = Classes.INTERFACES_OF.map( type ); + for( Type anInterface : interfaces ) + { + if( !types.contains( anInterface ) ) + { + types.add( anInterface ); + } + } + types.add( type ); + } + + if( current.getGenericSuperclass() != null ) + { + types.add( current.getGenericSuperclass() ); + } + + for( Type type : types ) + { + Class subClass; + if( type instanceof ParameterizedType ) + { + ParameterizedType pt = (ParameterizedType) type; + Type[] args = pt.getActualTypeArguments(); + Class clazz = (Class) pt.getRawType(); + TypeVariable[] vars = clazz.getTypeParameters(); + for( int i = 0; i < vars.length; i++ ) + { + TypeVariable var = vars[ i]; + Type mappedType = args[ i]; + mappings.put( var, mappedType ); + } + subClass = (Class) pt.getRawType(); + } + else + { + subClass = (Class) type; + } + + Type resolvedType = resolveTypeVariable( name, declaringClass, mappings, subClass ); + if( resolvedType != null ) + { + return resolvedType; + } + } + + return null; + } + + /** + * Get URI for a class. + * + * @param clazz class + * + * @return URI + * + * @throws NullPointerException if clazz is null + */ + @SuppressWarnings( "raw" ) + public static String toURI( final Class clazz ) + throws NullPointerException + { + return toURI( clazz.getName() ); + } + + /** + * Get URI for a class name. + *

+ * Example: + *

+ *

+ * Class name com.example.Foo$Bar is converted to URI urn:qi4j:com.example.Foo-Bar + *

+ * + * @param className class name + * + * @return URI + * + * @throws NullPointerException if className is null + */ + public static String toURI( String className ) + throws NullPointerException + { + className = normalizeClassToURI( className ); + return "urn:qi4j:type:" + className; + } + + /** + * Get class name from a URI + * + * @param uri URI + * + * @return class name + * + * @throws NullPointerException if uri is null + */ + public static String toClassName( String uri ) + throws NullPointerException + { + uri = uri.substring( "urn:qi4j:type:".length() ); + uri = denormalizeURIToClass( uri ); + return uri; + } + + public static String normalizeClassToURI( String className ) + { + return className.replace( '$', '-' ); + } + + public static String denormalizeURIToClass( String uriPart ) + { + return uriPart.replace( '-', '$' ); + } + + public static Specification modelTypeSpecification( final String className ) + { + return new Specification() + { + @Override + public boolean satisfiedBy( ModelDescriptor item ) + { + return matchesAny( new Specification() + { + @Override + public boolean satisfiedBy( String item ) + { + return item.equals( className ); + } + }, map( new Function, String>() + { + @Override + public String map( Class item ) + { + return item.getName(); + } + }, item.types() ) ); + } + }; + } + + @SuppressWarnings( "raw" ) + public static Specification exactTypeSpecification( final Class type ) + { + return new Specification() + { + @Override + public boolean satisfiedBy( ModelDescriptor item ) + { + return matchesAny( new Specification>() + { + @Override + public boolean satisfiedBy( Class item ) + { + return item.equals( type ); + } + }, item.types() ); + } + }; + } + + @SuppressWarnings( "raw" ) + public static Specification assignableTypeSpecification( final Class type ) + { + return new Specification() + { + @Override + public boolean satisfiedBy( ModelDescriptor item ) + { + return matchesAny( new Specification>() + { + @Override + @SuppressWarnings( "unchecked" ) + public boolean satisfiedBy( Class itemType ) + { + return !type.equals( itemType ) && type.isAssignableFrom( itemType ); + } + }, item.types() ); + } + }; + } + + @SuppressWarnings( "raw" ) + public static String toString( Iterable type ) + { + StringBuilder builder = new StringBuilder(); + builder.append( "[" ); + boolean first = true; + for( Class c : type ) + { + if( !first ) + { + builder.append( "," ); + } + first = false; + builder.append( c.getSimpleName() ); + } + builder.append( "]" ); + return builder.toString(); + } + + public static Function toClassName() + { + return new Function() + { + @Override + public String map( Type type ) + { + return RAW_CLASS.map( type ).getName(); + } + }; + } + + private Classes() + { + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/util/Constructors.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/util/Constructors.java b/core/api/src/main/java/org/qi4j/api/util/Constructors.java new file mode 100644 index 0000000..17062ca --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/util/Constructors.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.qi4j.api.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Type; +import org.qi4j.functional.Function; + +import static org.qi4j.functional.Iterables.iterable; + +/** + * Useful methods for handling Constructors. + */ +public final class Constructors +{ + public static final Function>> CONSTRUCTORS_OF = Classes.forClassHierarchy( new Function, Iterable>>() + { + @Override + public Iterable> map( Class type ) + { + return iterable( type.getDeclaredConstructors() ); + } + } ); +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/util/Dates.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/util/Dates.java b/core/api/src/main/java/org/qi4j/api/util/Dates.java new file mode 100644 index 0000000..3324df2 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/util/Dates.java @@ -0,0 +1,102 @@ +/* + * Copyright 2009 Niclas Hedhman. + * + * Licensed 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.qi4j.api.util; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +/** + * Useful methods for handling Dates. + */ +public final class Dates +{ + // Formatters are not thread-safe. Create one per thread + private static final ThreadLocal ISO8601 = new ThreadLocal() + { + @Override + protected DateFormat initialValue() + { + return new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" ); + } + }; + + private static final ThreadLocal ISO8601_UTC = new ThreadLocal() + { + @Override + protected DateFormat initialValue() + { + SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" ); + dateFormat.setTimeZone( TimeZone.getTimeZone( "UTC" ) ); + return dateFormat; + } + }; + + /** + * @param stringDate a string representing a date as either ISO8601, @millis@ or /Date() formats + * @return a Date + */ + public static Date fromString( String stringDate ) + { + try + { + Date date = ISO8601_UTC.get().parse( stringDate ); + return date; + } + catch( ParseException e ) + { + try + { + Date date = ISO8601.get().parse( stringDate ); + return date; + } + catch( ParseException e1 ) + { + // @millis@ format + if( stringDate.startsWith( "@" ) && stringDate.endsWith( "@" ) ) + { + long time = Long.parseLong( stringDate.substring( 1, stringDate.length() - 1 ) ); + Date date = new Date( time ); + return date; + } + else if( stringDate.startsWith( "/Date(" ) && stringDate.endsWith( ")/" ) ) // Microsoft format + { + long time = Long.parseLong( stringDate.substring( 6, stringDate.length() - 2 ) ); + Date date = new Date( time ); + return date; + } + throw new IllegalStateException( "Illegal date:" + stringDate ); + } + } + } + + /** + * @param date a Date + * @return String representation in ISO8601 UTC + */ + public static String toUtcString( Date date ) + { + return ISO8601_UTC.get().format( date ); + } + + private Dates() + { + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/util/Fields.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/util/Fields.java b/core/api/src/main/java/org/qi4j/api/util/Fields.java new file mode 100644 index 0000000..c68d131 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/util/Fields.java @@ -0,0 +1,51 @@ +/* + * 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.qi4j.api.util; + +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import org.qi4j.functional.Function; +import org.qi4j.functional.Function2; +import org.qi4j.functional.Iterables; + +import static org.qi4j.functional.Iterables.iterable; + +/** + * Useful methods for handling Fields. + */ +public final class Fields +{ + public static final Function2, String, Field> FIELD_NAMED = new Function2, String, Field>() + { + @Override + public Field map( Class aClass, String name ) + { + return Iterables.first( Iterables.filter( Classes.memberNamed( name ), FIELDS_OF.map( aClass ) ) ); + } + }; + + public static final Function> FIELDS_OF = Classes.forClassHierarchy( new Function, Iterable>() + { + @Override + public Iterable map( Class type ) + { + return iterable( type.getDeclaredFields() ); + } + } ); +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/util/ListMap.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/util/ListMap.java b/core/api/src/main/java/org/qi4j/api/util/ListMap.java new file mode 100644 index 0000000..56778b8 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/util/ListMap.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2008, Rickard Öberg. All Rights Reserved. + * + * Licensed 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.qi4j.api.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Map whose values are Lists of things. Create + * one ArrayList for each key that is added. The list does not allow + * duplicates. + */ +public final class ListMap + extends HashMap> +{ + public void add( K key, V value ) + { + List list = get( key ); + if( list == null ) + { + list = new ArrayList(); + put( key, list ); + } + if( !list.contains( value ) ) + { + list.add( value ); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/util/Methods.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/util/Methods.java b/core/api/src/main/java/org/qi4j/api/util/Methods.java new file mode 100644 index 0000000..93b78cf --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/util/Methods.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.qi4j.api.util; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import org.qi4j.functional.Function; +import org.qi4j.functional.Specification; + +import static org.qi4j.functional.Iterables.iterable; + +/** + * Useful methods for handling Methods. + */ +public class Methods +{ + public static final Specification HAS_METHODS = new Specification() + { + @Override + public boolean satisfiedBy( Type item ) + { + return Classes.RAW_CLASS.map( item ).getDeclaredMethods().length > 0; + } + }; + + public static final Function> METHODS_OF = Classes.forTypes( new Function>() + { + @Override + public Iterable map( Type type ) + { + return iterable( Classes.RAW_CLASS.map( type ).getDeclaredMethods() ); + } + } ); +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/util/NullArgumentException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/util/NullArgumentException.java b/core/api/src/main/java/org/qi4j/api/util/NullArgumentException.java new file mode 100644 index 0000000..58e514f --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/util/NullArgumentException.java @@ -0,0 +1,56 @@ +/* Copyright 2007 Niclas Hedhman. + * + * Licensed 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.qi4j.api.util; + +/** + * Thrown if an argument to a method was null, and the method required + * it to be non-null. + */ +public class NullArgumentException + extends IllegalArgumentException +{ + private static final long serialVersionUID = 4815431779868729780L; + + private NullArgumentException( String message ) + { + super( message ); + } + + public static void validateNotNull( String parameterName, Object value ) + { + if( value != null ) + { + return; + } + String message = parameterName + " was null."; + throw new NullArgumentException( message ); + } + + public static void validateNotEmpty( String parameterName, String value ) + { + if( value == null ) + { + String message = parameterName + " was null."; + throw new NullArgumentException( message ); + } + if( value.length() == 0 ) + { + String message = parameterName + " was empty."; + throw new NullArgumentException( message ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/util/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/util/package.html b/core/api/src/main/java/org/qi4j/api/util/package.html new file mode 100644 index 0000000..ea75db3 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/util/package.html @@ -0,0 +1,21 @@ + + + +

API Utilities.

+ + http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/NoSuchValueException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/NoSuchValueException.java b/core/api/src/main/java/org/qi4j/api/value/NoSuchValueException.java new file mode 100644 index 0000000..256e9fc --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/NoSuchValueException.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2008, Niclas Hedhman. All Rights Reserved. + * + * Licensed 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.qi4j.api.value; + +import org.qi4j.api.composite.NoSuchCompositeException; + +/** + * Thrown when no visible value of the requested type is found. + */ +public class NoSuchValueException + extends NoSuchCompositeException +{ + public NoSuchValueException( String valueType, String moduleName ) + { + super( "ValueComposite", valueType, moduleName ); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/ValueBuilder.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/ValueBuilder.java b/core/api/src/main/java/org/qi4j/api/value/ValueBuilder.java new file mode 100644 index 0000000..1073a0d --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/ValueBuilder.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2009, Rickard Öberg. All Rights Reserved. + * + * Licensed 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.qi4j.api.value; + +import org.qi4j.api.association.AssociationStateHolder; +import org.qi4j.api.common.ConstructionException; + +/** + * Builder for Values. + */ +public interface ValueBuilder +{ + AssociationStateHolder state(); + + /** + * Get a representation of the state for the new Value. + * It is possible to access and update properties and associations, + * even immutable ones since the builder represents the initial state. + * + * @return a mutable instance of the Value type + */ + T prototype(); + + /** + * Get a representation of the state of the given type for the new ValueComposite. + * This is primarily used if you want to provide state for a private mixin type. + * + * @param mixinType the mixin which you want to provide state for + * + * @return a proxy implementing the given mixin type + */ + K prototypeFor( Class mixinType ); + + /** + * Create a new Composite instance. + * + * @return a new Composite instance + * + * @throws org.qi4j.api.common.ConstructionException + * thrown if it was not possible to instantiate the Composite + */ + T newInstance() + throws ConstructionException; +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/ValueBuilderFactory.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/ValueBuilderFactory.java b/core/api/src/main/java/org/qi4j/api/value/ValueBuilderFactory.java new file mode 100644 index 0000000..57c4e29 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/ValueBuilderFactory.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2009, Rickard Öberg. All Rights Reserved. + * + * Licensed 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.qi4j.api.value; + +import java.util.Map; +import org.qi4j.api.association.AssociationDescriptor; +import org.qi4j.api.common.ConstructionException; +import org.qi4j.api.entity.EntityReference; +import org.qi4j.api.property.PropertyDescriptor; +import org.qi4j.functional.Function; + +/** + * Factory for Values and ValueBuilders. + */ +public interface ValueBuilderFactory +{ + + /** + * Instantiate a Value of the given type. + * + * @param valueType the Value type to instantiate + * + * @return a new Value instance + * + * @throws NoSuchValueException if no value extending the mixinType has been registered + * @throws ConstructionException if the value could not be instantiated + */ + T newValue( Class valueType ) + throws NoSuchValueException, ConstructionException; + + /** + * Create a builder for creating new Values that implements the given Value type. + *

The returned ValueBuilder can be reused to create several Values instances.

+ * + * @param valueType an interface that describes the Composite to be instantiated + * + * @return a ValueBuilder for creation of ValueComposites implementing the interface + * + * @throws NoSuchValueException if no value extending the mixinType has been registered + */ + ValueBuilder newValueBuilder( Class valueType ) + throws NoSuchValueException; + + /** + * Create a builder for creating a new Value starting with the given prototype. + *

The returned ValueBuilder can only be used ONCE.

+ * + * @param prototype a prototype the builder will use + * + * @return a ValueBuilder for creation of ValueComposites implementing the interface of the prototype + * + * @throws NoSuchValueException if no value extending the mixinType has been registered + */ + ValueBuilder newValueBuilderWithPrototype( T prototype ); + + /** + * Create a builder for creating a new Value starting with the given state. + *

The returned ValueBuilder can only be used ONCE.

+ * + * @param mixinType an interface that describes the Composite to be instantiated + * @param propertyFunction a function providing the state of properties + * @param associationFunction a function providing the state of associations + * @param manyAssociationFunction a function providing the state of many associations + * @param namedAssociationFunction a function providing the state of named associations + * + * @return a ValueBuilder for creation of ValueComposites implementing the interface + * + * @throws NoSuchValueException if no value extending the mixinType has been registered + */ + ValueBuilder newValueBuilderWithState( Class mixinType, + Function propertyFunction, + Function associationFunction, + Function> manyAssociationFunction, + Function> namedAssociationFunction ); + + /** + * Instantiate a Value of the given type using the serialized state given as String. + * + * @param valueType the Value type to instantiate + * @param serializedState the state of the Value + * + * @return a new Value instance + * + * @throws NoSuchValueException if no value extending the mixinType has been registered + * @throws ConstructionException if the value could not be instantiated + */ + T newValueFromSerializedState( Class valueType, String serializedState ); + +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/ValueBuilderTemplate.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/ValueBuilderTemplate.java b/core/api/src/main/java/org/qi4j/api/value/ValueBuilderTemplate.java new file mode 100644 index 0000000..11cbddb --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/ValueBuilderTemplate.java @@ -0,0 +1,43 @@ +/* + * 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.qi4j.api.value; + +import org.qi4j.api.structure.Module; + +/** + * Builder template for Values. + */ +public abstract class ValueBuilderTemplate +{ + Class type; + + protected ValueBuilderTemplate( Class type ) + { + this.type = type; + } + + protected abstract void build( T prototype ); + + public T newInstance( Module module ) + { + ValueBuilder builder = module.newValueBuilder( type ); + build( builder.prototype() ); + return builder.newInstance(); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/ValueComposite.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/ValueComposite.java b/core/api/src/main/java/org/qi4j/api/value/ValueComposite.java new file mode 100644 index 0000000..7d5f48f --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/ValueComposite.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2008, Rickard Öberg. All Rights Reserved. + * + * Licensed 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.qi4j.api.value; + +import org.qi4j.api.association.AssociationMixin; +import org.qi4j.api.association.ManyAssociationMixin; +import org.qi4j.api.association.NamedAssociationMixin; +import org.qi4j.api.composite.Composite; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.property.Immutable; + +/** + * ValueComposites are Composites that has state, and equality is defined from its values and not any identity nor + * instance references. + * + *
    + *
  • No Identity
  • + *
  • No Lifecycle
  • + *
  • Immutable
  • + *
  • equals()/hashCode() operates on the Properties
  • + *
  • Can have property and associations methods.
  • + *
  • Can not reference Services
  • + *
  • Can not have @Uses
  • + *
+ */ +@Immutable +@Mixins( { AssociationMixin.class, ManyAssociationMixin.class, NamedAssociationMixin.class } ) +public interface ValueComposite + extends Composite +{ +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/ValueDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/ValueDescriptor.java b/core/api/src/main/java/org/qi4j/api/value/ValueDescriptor.java new file mode 100644 index 0000000..710de89 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/ValueDescriptor.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2009, Rickard Öberg. All Rights Reserved. + * + * Licensed 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.qi4j.api.value; + +import org.qi4j.api.association.AssociationStateDescriptor; +import org.qi4j.api.composite.CompositeDescriptor; +import org.qi4j.api.composite.StatefulCompositeDescriptor; +import org.qi4j.api.type.ValueCompositeType; + +/** + * Descriptor for ValueComposites. + */ +public interface ValueDescriptor + extends CompositeDescriptor, StatefulCompositeDescriptor +{ + ValueCompositeType valueType(); + + @Override + AssociationStateDescriptor state(); +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/ValueDeserializer.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/ValueDeserializer.java b/core/api/src/main/java/org/qi4j/api/value/ValueDeserializer.java new file mode 100644 index 0000000..175b176 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/ValueDeserializer.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2012, Paul Merlin. All Rights Reserved. + * + * Licensed 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.qi4j.api.value; + +import java.io.InputStream; +import org.qi4j.api.type.ValueType; +import org.qi4j.functional.Function; +import org.qi4j.functional.Function2; + +/** + * Use a ValueDeserializer to create new values instances from serialized state. + * + *

+ * Serialized state must be one of: + *

+ *
    + *
  • a ValueComposite,
  • + *
  • an EntityReference,
  • + *
  • a Collection,
  • + *
  • a Map,
  • + *
  • a Plain Value.
  • + *
+ *

+ * Nested plain values, EntityReferences, Collections, Maps, ValueComposites are supported. + * EntityReferences are deserialized as their identity string. + *

+ *

+ * Plain values can be one of: + *

+ *
    + *
  • String,
  • + *
  • Character or char,
  • + *
  • Boolean or boolean,
  • + *
  • Integer or int,
  • + *
  • Long or long,
  • + *
  • Short or short,
  • + *
  • Byte or byte,
  • + *
  • Float or float,
  • + *
  • Double or double,
  • + *
  • BigInteger,
  • + *
  • BigDecimal,
  • + *
  • Date,
  • + *
  • DateTime (JodaTime),
  • + *
  • LocalDateTime (JodaTime),
  • + *
  • LocalDate (JodaTime).
  • + *
+ *

+ * Values of unknown types and all arrays are considered as {@link java.io.Serializable} and by so are deserialized + * from base64 encoded bytes using pure Java serialization. If it happens that the input is invalid, a + * ValueSerializationException is thrown. + *

+ *

+ * Having type information in the serialized payload allows to keep actual ValueComposite types and by so + * circumvent {@link org.qi4j.api.composite.AmbiguousTypeException} when deserializing. + *

+ */ +public interface ValueDeserializer +{ + + /** + * Factory method for a typed deserialize function. + * + *

The returned Function may throw {@link ValueSerializationException}.

+ * + * @param type the value type + * @param the parametrized function return type + * @return a deserialization function + */ + Function deserialize( Class type ); + + /** + * Factory method for a typed deserialize function. + * + *

The returned Function may throw {@link ValueSerializationException}.

+ * + * @param valueType the value type + * @param the parametrized function return type + * @return a deserialization function + */ + Function deserialize( ValueType valueType ); + + /** + * Factory method for an untyped deserialize function. + * + *

The returned Function may throw {@link ValueSerializationException}.

+ * + * @param the parametrized function return type + * @return a deserialization function + */ + Function2 deserialize(); + + /** + * Deserialize a value from a state. + * + * @param the parametrized returned type + * @param type the value type + * @param input the state + * @return the value + * @throws ValueSerializationException if the deserialization failed + */ + T deserialize( Class type, String input ) + throws ValueSerializationException; + + /** + * Deserialize a value from a state. + * + * @param the parametrized returned type + * @param valueType the value type + * @param input the state + * @return the value + * @throws ValueSerializationException if the deserialization failed + */ + T deserialize( ValueType valueType, String input ) + throws ValueSerializationException; + + /** + * Deserialize a value from a state. + * + * @param the parametrized returned type + * @param type the value type + * @param input the state stream + * @return the value + * @throws ValueSerializationException if the deserialization failed + */ + T deserialize( Class type, InputStream input ) + throws ValueSerializationException; + + /** + * Deserialize a value from a state. + * + * @param the parametrized returned type + * @param valueType the value type + * @param input the state stream + * @return the value + * @throws ValueSerializationException if the deserialization failed + */ + T deserialize( ValueType valueType, InputStream input ) + throws ValueSerializationException; +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/ValueSerialization.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/ValueSerialization.java b/core/api/src/main/java/org/qi4j/api/value/ValueSerialization.java new file mode 100644 index 0000000..31a4af0 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/ValueSerialization.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2012, Paul Merlin. All Rights Reserved. + * + * Licensed 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.qi4j.api.value; + +/** + * ValueSerialization API. + * + * See {@link ValueSerializer} and {@link ValueDeserializer}. + */ +public interface ValueSerialization + extends ValueSerializer, ValueDeserializer +{ + + /** + * Serialization format @Service tags. + * + *

+ * ValueSerialization implementations should be tagged with theses at assembly time so that consumers can + * specify which format they need. + *

+ */ + interface Formats + { + + /** + * Tag a ValueSerialization service that support the JSON format. + */ + String JSON = "json"; + /** + * Tag a ValueSerialization service that support the XML format. + */ + String XML = "xml"; + /** + * Tag a ValueSerialization service that support the YAML format. + */ + String YAML = "yaml"; + } + +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/ValueSerializationException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/ValueSerializationException.java b/core/api/src/main/java/org/qi4j/api/value/ValueSerializationException.java new file mode 100644 index 0000000..e1f3d44 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/ValueSerializationException.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012, Paul Merlin. All Rights Reserved. + * + * Licensed 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.qi4j.api.value; + +/** + * Thrown when an error occur during value state (de)serialization. + */ +public class ValueSerializationException + extends RuntimeException +{ + + private static final long serialVersionUID = 1L; + + public ValueSerializationException() + { + super(); + } + + public ValueSerializationException( String message ) + { + super( message ); + } + + public ValueSerializationException( String message, Throwable cause ) + { + super( message, cause ); + } + + public ValueSerializationException( Throwable cause ) + { + super( cause.getClass().getName() + ": " + cause.getMessage(), cause ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/ValueSerializer.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/ValueSerializer.java b/core/api/src/main/java/org/qi4j/api/value/ValueSerializer.java new file mode 100644 index 0000000..337c37e --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/ValueSerializer.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2012-2014, Paul Merlin. All Rights Reserved. + * + * Licensed 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.qi4j.api.value; + +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; +import org.qi4j.api.composite.AmbiguousTypeException; +import org.qi4j.functional.Function; + +/** + * Use a ValueSerializer to serialize values state. + * + *

+ * Serialized object must be one of: + *

+ *
    + *
  • a ValueComposite,
  • + *
  • an EntityComposite or EntityReference,
  • + *
  • an Iterable,
  • + *
  • a Map,
  • + *
  • a Plain Value.
  • + *
+ *

+ * Nested plain values, EntityReferences, Iterables, Maps, ValueComposites and EntityComposites are supported. + * EntityComposites and EntityReferences are serialized as their identity string. + *

+ *

+ * Plain values can be one of: + *

+ *
    + *
  • String,
  • + *
  • Character or char,
  • + *
  • Boolean or boolean,
  • + *
  • Integer or int,
  • + *
  • Long or long,
  • + *
  • Short or short,
  • + *
  • Byte or byte,
  • + *
  • Float or float,
  • + *
  • Double or double,
  • + *
  • BigInteger,
  • + *
  • BigDecimal,
  • + *
  • Date,
  • + *
  • DateTime (JodaTime),
  • + *
  • LocalDateTime (JodaTime),
  • + *
  • LocalDate (JodaTime).
  • + *
+ *

+ * Values of unknown types and all arrays are considered as {@link java.io.Serializable} and by so are serialized to + * base64 encoded bytes using pure Java serialization. If it happens that the value is not Serializable, a + * ValueSerializationException is thrown. + *

+ *

+ * Having type information in the serialized payload allows to keep actual ValueComposite types and by so + * circumvent {@link AmbiguousTypeException} when deserializing. + *

+ */ +public interface ValueSerializer +{ + + /** + * Factory method for a serialize function. + * + * @param the parametrized function input type + * @return a serialization function. + */ + Function serialize(); + + /** + * Factory method for a serialize function. + * + * @param the parametrized function input type + * @param options ValueSerializer Options + * @return a serialization function. + */ + Function serialize( Options options ); + + /** + * Factory method for a serialize function. + * + * @param the parametrized function input type + * @param includeTypeInfo if type information should be included in the output + * @return a serialization function. + */ + @Deprecated + Function serialize( boolean includeTypeInfo ); + + /** + * Serialize the state of a value with type information. + * + * @param object an Object to serialize + * @return the state + * @throws ValueSerializationException if the Value serialization failed + */ + String serialize( Object object ) + throws ValueSerializationException; + + /** + * Serialize the state of a value. + * + * @param options ValueSerializer Options + * @param object an Object to serialize + * @return the state + * @throws ValueSerializationException if the Value serialization failed + */ + String serialize( Options options, Object object ) + throws ValueSerializationException; + + /** + * Serialize the state of a value. + * + * @param object an Object to serialize + * @param includeTypeInfo if type information should be included in the output + * @return the state + * @throws ValueSerializationException if the Value serialization failed + */ + @Deprecated + String serialize( Object object, boolean includeTypeInfo ) + throws ValueSerializationException; + + /** + * Serialize the state of a value with type information. + * + * @param object an Object to serialize + * @param output that will be used as output + * @throws ValueSerializationException if the Value serialization failed + */ + void serialize( Object object, OutputStream output ) + throws ValueSerializationException; + + /** + * Serialize the state of a value. + * + * @param options ValueSerializer Options + * @param object an Object to serialize + * @param output that will be used as output + * @throws ValueSerializationException if the Value serialization failed + */ + void serialize( Options options, Object object, OutputStream output ) + throws ValueSerializationException; + + /** + * Serialize the state of a value. + * + * @param object an Object to serialize + * @param output that will be used as output + * @param includeTypeInfo if type information should be included in the output + * @throws ValueSerializationException if the Value serialization failed + */ + @Deprecated + void serialize( Object object, OutputStream output, boolean includeTypeInfo ) + throws ValueSerializationException; + + /** + * Serialization options. + */ + final class Options + { + /** + * Boolean flag to include type information. + * Default to TRUE. + */ + public static final String INCLUDE_TYPE_INFO = "includeTypeInfo"; + public static final String MAP_ENTRIES_AS_OBJECTS = "mapentriesasobjects"; + private final Map options = new HashMap<>(); + + /** + * Create new default ValueSerializer Options. + */ + public Options() + { + this.options.put( INCLUDE_TYPE_INFO, "true" ); + this.options.put( MAP_ENTRIES_AS_OBJECTS, "false" ); + } + + /** + * Set {@link #INCLUDE_TYPE_INFO} option to TRUE. + * @return This + */ + public Options withTypeInfo() + { + return put( INCLUDE_TYPE_INFO, true ); + } + + /** + * Set {@link #INCLUDE_TYPE_INFO} option to FALSE. + * @return This + */ + public Options withoutTypeInfo() + { + return put( INCLUDE_TYPE_INFO, false ); + } + + public Options withMapEntriesAsObjects() + { + return put( MAP_ENTRIES_AS_OBJECTS, true ); + } + + public Options withMapEntriesAsKeyValuePairs() + { + return put( MAP_ENTRIES_AS_OBJECTS, false ); + } + + /** + * Get Boolean option value. + * @param option The option + * @return The boolean value of the option, or null if absent + */ + public Boolean getBoolean( String option ) + { + if( !options.containsKey( option ) ) + { + return null; + } + return Boolean.valueOf( options.get( option ) ); + } + + /** + * Get Integer option value. + * @param option The option + * @return The integer value of the option, or null if absent + */ + public Integer getInteger( String option ) + { + if( !options.containsKey( option ) ) + { + return null; + } + return Integer.valueOf( options.get( option ) ); + } + + /** + * Get String option value. + * @param option The option + * @return The string value of the option, or null if absent + */ + public String getString( String option ) + { + return options.get( option ); + } + + /** + * Put an option String value. + * @param option The option + * @param value The value + * @return This Options instance + */ + public Options put( String option, String value ) + { + if( value == null ) + { + return remove( option ); + } + options.put( option, value ); + return this; + } + + /** + * Put an option boolean value. + * @param option The option + * @param value The value + * @return This Options instance + */ + public Options put( String option, Boolean value ) + { + if( value == null ) + { + return remove( option ); + } + options.put( option, Boolean.toString( value ) ); + return this; + } + + /** + * Put an option Integer value. + * @param option The option + * @param value The value + * @return This Options instance + */ + public Options put( String option, Integer value ) + { + if( value == null ) + { + return remove( option ); + } + options.put( option, value.toString() ); + return this; + } + + /** + * Remove an option value. + * @param option The option + * @return This Options instance + */ + public Options remove( String option ) + { + options.remove( option ); + return this; + } + + /** + * Get all defined options as a Map. + * @return All defined options in a new Map + */ + public Map toMap() + { + return new HashMap<>( options ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/value/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/value/package.html b/core/api/src/main/java/org/qi4j/api/value/package.html new file mode 100644 index 0000000..540b3f6 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/value/package.html @@ -0,0 +1,21 @@ + + + +

Value API.

+ + http://git-wip-us.apache.org/repos/asf/polygene-java/blob/a789141d/core/api/src/test/java/org/apache/zest/api/OperatorsTest.java ---------------------------------------------------------------------- diff --git a/core/api/src/test/java/org/apache/zest/api/OperatorsTest.java b/core/api/src/test/java/org/apache/zest/api/OperatorsTest.java deleted file mode 100644 index 2f4301a..0000000 --- a/core/api/src/test/java/org/apache/zest/api/OperatorsTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.zest.api; - -import org.junit.Assert; -import org.junit.Test; -import org.apache.zest.api.activation.ActivationException; -import org.apache.zest.api.composite.Composite; -import org.apache.zest.api.entity.EntityBuilder; -import org.apache.zest.api.entity.EntityComposite; -import org.apache.zest.api.property.Property; -import org.apache.zest.api.query.QueryBuilder; -import org.apache.zest.api.query.QueryExpressions; -import org.apache.zest.api.unitofwork.UnitOfWork; -import org.apache.zest.api.unitofwork.UnitOfWorkCompletionException; -import org.apache.zest.api.value.ValueComposite; -import org.apache.zest.bootstrap.AssemblyException; -import org.apache.zest.bootstrap.ModuleAssembly; -import org.apache.zest.bootstrap.SingletonAssembler; -import org.apache.zest.functional.Iterables; -import org.apache.zest.functional.Specification; -import org.apache.zest.test.EntityTestAssembler; - -/** - * TODO - */ -public class OperatorsTest -{ - @Test - public void testOperators() - throws UnitOfWorkCompletionException, ActivationException, AssemblyException - { - SingletonAssembler assembler = new SingletonAssembler() - { - @Override - public void assemble( ModuleAssembly module ) - throws AssemblyException - { - new EntityTestAssembler().assemble( module ); - - module.entities( TestEntity.class ); - module.values( TestValue.class ); - module.forMixin( TestEntity.class ).declareDefaults().foo().set( "Bar" ); - module.forMixin( TestValue.class ).declareDefaults().bar().set( "Xyz" ); - } - }; - - UnitOfWork uow = assembler.module().newUnitOfWork(); - - try - { - EntityBuilder entityBuilder = uow.newEntityBuilder( TestEntity.class, "123" ); - entityBuilder.instance().value().set( assembler.module().newValue( TestValue.class ) ); - TestEntity testEntity = entityBuilder.newInstance(); - - uow.complete(); - uow = assembler.module().newUnitOfWork(); - - Iterable entities = Iterables.iterable( testEntity = uow.get( testEntity ) ); - - QueryBuilder builder = assembler.module().newQueryBuilder( TestEntity.class ); - - { - Specification where = QueryExpressions.eq( QueryExpressions.templateFor( TestEntity.class ) - .foo(), "Bar" ); - Assert.assertTrue( where.satisfiedBy( testEntity ) ); - System.out.println( where ); - } - { - Specification where = QueryExpressions.eq( QueryExpressions.templateFor( TestEntity.class ) - .value() - .get() - .bar(), "Xyz" ); - Assert.assertTrue( where.satisfiedBy( testEntity ) ); - System.out.println( where ); - - Assert.assertTrue( builder.where( where ).newQuery( entities ).find().equals( testEntity ) ); - } - } - finally - { - uow.discard(); - } - } - - public interface TestEntity - extends EntityComposite - { - Property foo(); - - Property value(); - } - - public interface TestValue - extends ValueComposite - { - Property bar(); - } -}