/* * Copyright 2008 Red Hat, Inc. and/or its affiliates. * * 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.drools.core.factmodel; import org.drools.core.phreak.Reactive; import org.kie.api.definition.type.Annotation; import org.kie.api.definition.type.FactField; import org.kie.api.definition.type.FactType; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * Declares a class to be dynamically created */ public class ClassDefinition implements FactType { public static enum TRAITING_MODE { NONE, BASIC, LOGICAL } private String className; private String superClass; private String[] interfaces; private transient Class< ? > definedClass; private TRAITING_MODE traitable; private boolean abstrakt = false; private Map<String, Object> metaData; private LinkedHashMap<String, FieldDefinition> fields = new LinkedHashMap<String, FieldDefinition>(); private Map<String, AnnotationDefinition> annotations; private Map<String, List<String>> modifiedPropsByMethod; public ClassDefinition() { this( null, null, null ); } public ClassDefinition( String className ) { this( className, null, null ); } public ClassDefinition( String className, String superClass ) { this( className, superClass, null ); } public ClassDefinition( String className, String superClass, String[] interfaces ) { this.setClassName( className ); this.setSuperClass( superClass ); this.setInterfaces( interfaces ); } public ClassDefinition( Class<?> cls ) { this.definedClass = cls; this.setClassName( cls.getCanonicalName() ); this.setSuperClass( cls.getSuperclass() != null ? cls.getSuperclass().getCanonicalName() : null ); String[] interfaces = new String[cls.getInterfaces().length]; int i = 0; for (Class<?> interfaze : cls.getInterfaces()) { interfaces[i++] = interfaze.getCanonicalName(); } this.setInterfaces( interfaces ); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.className = (String) in.readObject(); this.superClass = (String) in.readObject(); this.interfaces = (String[]) in.readObject(); this.fields = (LinkedHashMap<String, FieldDefinition>) in.readObject(); this.annotations = (Map<String, AnnotationDefinition>) in.readObject(); this.modifiedPropsByMethod = (Map<String, List<String>>) in.readObject(); this.traitable = (ClassDefinition.TRAITING_MODE) in.readObject(); this.abstrakt = in.readBoolean(); this.metaData = (HashMap<String,Object>) in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject( this.className ); out.writeObject( this.superClass ); out.writeObject( this.interfaces ); out.writeObject( this.fields ); out.writeObject( this.annotations ); out.writeObject( this.modifiedPropsByMethod); out.writeObject( this.traitable ); out.writeBoolean( this.abstrakt ); out.writeObject( this.metaData ); } /** * @return Returns the name. */ public final String getClassName() { return this.className; } /** * @param className The name to set. */ public final void setClassName(final String className) { this.className = className; } /** * @return Returns the className. */ public final Class< ? > getDefinedClass() { return definedClass; } /** * @param definedClass The class to set. */ public void setDefinedClass(final Class< ? > definedClass) { this.definedClass = definedClass; } /** * Adds a field definition to this class * @param attr */ public final void addField(FieldDefinition attr) { this.fields.put( attr.getName(), attr ); } /** * @return Returns an unmodifiable collection of field definitions */ public final Collection<FieldDefinition> getFieldsDefinitions() { return Collections.unmodifiableCollection( this.fields.values() ); } /** * Returns the field definition object for the given field name * * @param fieldName * @return */ public final FieldDefinition getField(final String fieldName) { return this.fields.get( fieldName ); } public FieldDefinition getFieldByAlias( String alias ) { for ( FactField factField : getFields() ) { FieldDefinition def = (FieldDefinition) factField; if ( def.resolveAlias().equals( alias ) ) { return def; } } return null; } /** * Returns the field at position index, as defined by the builder using the @position annotation * @param index * @return the index-th field */ public FieldDefinition getField(int index) { if (index >= fields.size() || index < 0) { return null; } Iterator<FieldDefinition> iter = fields.values().iterator(); for (int j = 0; j < index ; j++) { iter.next(); } return iter.next(); } /** * @return Returns the interfaces. */ public final String[] getInterfaces() { return interfaces; } /** * @param interfaces The interfaces to set. */ public final void setInterfaces(String[] interfaces) { this.interfaces = (interfaces != null) ? interfaces : new String[0]; } /** * @return Returns the superClass. */ public final String getSuperClass() { return superClass; } /** * @param superClass The superClass to set. */ public final void setSuperClass(final String superClass) { this.superClass = (superClass != null) ? superClass : "java.lang.Object"; } public String getName() { return getClassName(); } public String getSimpleName() { return getClassName().substring( getClassName().lastIndexOf( '.' ) + 1 ); } public String getPackageName() { return getClassName().substring( 0, getClassName().lastIndexOf( '.' ) ); } public Object newInstance() throws InstantiationException, IllegalAccessException { return this.definedClass.newInstance(); } public Class< ? > getFactClass() { return getDefinedClass(); } public List<FactField> getFields() { return new ArrayList<FactField>( fields.values() ); } public Object get(Object bean, String field) { FieldDefinition fieldDefinition = getField( field ); return fieldDefinition != null ? fieldDefinition.getFieldAccessor().getValue( bean ) : null; } public void set(Object bean, String field, Object value) { FieldDefinition fieldDefinition = getField( field ); if (fieldDefinition != null) { fieldDefinition.getFieldAccessor().setValue( bean, value ); } } public Map<String, Object> getAsMap(Object bean) { Map<String, Object> m = new HashMap<String, Object>( fields.size() ); for (Map.Entry<String, FieldDefinition> ent : this.fields.entrySet()) { Object val = ent.getValue().getFieldAccessor().getValue(bean); m.put(ent.getKey(), val); } return m; } public void setFromMap(Object bean, Map<String, Object> data) { for (Map.Entry<String, Object> ent : data.entrySet()) { set(bean, ent.getKey(), ent.getValue()); } } public void addAnnotation(AnnotationDefinition annotationDefinition) { if (this.annotations == null) { this.annotations = new HashMap<String, AnnotationDefinition>(); } this.annotations.put( annotationDefinition.getName(), annotationDefinition ); } public Collection<AnnotationDefinition> getAnnotations() { return annotations != null ? annotations.values() : Collections.<AnnotationDefinition>emptyList(); } public AnnotationDefinition getAnnotation(Class<?> annotationClass) { return annotations != null ? annotations.get(annotationClass.getName()) : null; } public List<Annotation> getClassAnnotations() { return Collections.unmodifiableList( new ArrayList( getAnnotations() ) ); } public Map<String, Object> getMetaData() { return metaData; } public void addMetaData( String key, Object value ) { if ( this.metaData == null ) { metaData = new HashMap<String,Object>(); } metaData.put( key, value ); } public void addModifiedPropsByMethod(Method method, List<String> props) { if (modifiedPropsByMethod == null) { modifiedPropsByMethod = new HashMap<String, List<String>>(); } String methodName = modifiedPropsByMethodKey(method); modifiedPropsByMethod.put(methodName, props); } public List<String> getModifiedPropsByMethod(Method method) { return getModifiedPropsByMethod(method.getName(), method.getParameterTypes().length ); } public List<String> getModifiedPropsByMethod(String methodName, int args) { if (modifiedPropsByMethod == null) { return null; } List<String> byExactNumberOfArgs = modifiedPropsByMethod.get( methodName + "_" + args ); List<String> bestEffortVarArgs = modifiedPropsByMethod.get( methodName + "_*" ); if ( byExactNumberOfArgs != null ) { return byExactNumberOfArgs; } return bestEffortVarArgs; // << indeed maybe null } public static String modifiedPropsByMethodKey(Method method) { return method.getName() + "_" + ( method.isVarArgs() ? "*" : method.getParameterTypes().length ); } public boolean isReactive() { return getAnnotation( Reactive.class ) != null; } public boolean isTraitable() { return traitable != null && traitable != TRAITING_MODE.NONE; } public void setTraitable( boolean traitable ) { setTraitable( traitable, false ); } public void setTraitable( boolean traitable, boolean enableLogical ) { if ( ! traitable ) { this.traitable = TRAITING_MODE.NONE; } else { this.traitable = enableLogical ? TRAITING_MODE.LOGICAL : TRAITING_MODE.BASIC; } } public boolean isFullTraiting() { return this.traitable == TRAITING_MODE.LOGICAL; } public boolean isAbstrakt() { return abstrakt; } public void setAbstrakt(boolean abstrakt) { this.abstrakt = abstrakt; } public String toString() { return "ClassDefinition{" + "className='" + className + '\'' + ", superClass='" + superClass + '\'' + ", interfaces=" + (interfaces == null ? null : Arrays.asList(interfaces)) + ", definedClass=" + definedClass + ", traitable=" + traitable + ", abstract=" + abstrakt + ", fields=" + fields + ", annotations=" + annotations + '}'; } }