/* * Copyright 2015 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. * * 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.metadata; import java.io.Serializable; import java.net.URI; import java.util.Collection; import java.util.Collections; import java.util.List; import org.drools.core.factmodel.traits.TraitProxy; import org.drools.core.impl.InternalKnowledgeBase; import org.drools.core.reteoo.PropertySpecificUtil; import org.drools.core.util.ClassUtils; import org.drools.core.util.bitmask.BitMask; import static org.drools.core.reteoo.PropertySpecificUtil.setPropertyOnMask; public abstract class ModifyLiteral<T> extends AbstractWMTask<T> implements Modify<T>, Serializable { private T target; protected ModifyTaskLiteral<T,?,?> task; protected BitMask modificationMask; protected URI key; protected Object[] with; protected BitMask[] extraMasks; protected abstract MetaClass<T> getMetaClassInfo(); public ModifyLiteral( T target, With[] with ) { this.target = target; switch ( with.length ) { case 0 : this.with = null; this.extraMasks = null; return; case 1 : this.with = with[ 0 ].getArgs(); break; default : mergeWiths( with ); } this.extraMasks = new BitMask[ this.with.length ]; } protected void mergeWiths( With[] with ) { int n = 0; for ( int j = 0; j < with.length; j++ ) { n += with[ j ].getArgs().length; } this.with = new Object[ n ]; n = 0; for ( int j = 0; j < with.length; j++ ) { System.arraycopy( with[ j ].getArgs(), 0, this.with, n, with[ j ].getArgs().length ); n += with[ j ].getArgs().length; } } @Override public ModifyLiteral<T> getSetters() { return this; } public T getTarget() { return target; } void setTarget( T target ) { this.target = target; } public ModifyTask getSetterChain() { return task; } @Override public Object getTargetId() { return MetadataContainer.getIdentifier( target ); } @Override public Object[] getAdditionalUpdates() { return with; } @Override public BitMask getAdditionalUpdatesModificationMask( int j ) { return extraMasks[ j ]; } @Override public KIND kind() { return KIND.MODIFY; } public T call( T object ) { setTarget( object ); return call(); } public T call() { computeModificationMasks( null ); task.call( target ); return target; } public T call( InternalKnowledgeBase knowledgeBase ) { computeModificationMasks( knowledgeBase ); task.call( target ); return target; } protected void computeModificationMasks( InternalKnowledgeBase knowledgeBase ) { List<String> settableProperties = getAccessibleProperties( target, knowledgeBase ); modificationMask = PropertySpecificUtil.getEmptyPropertyReactiveMask( settableProperties.size() ); if ( with != null ) { List<String>[] inverseSettableProperties = new List[ with.length ]; for ( int j = 0; j < with.length; j++ ) { inverseSettableProperties[ j ] = getAccessibleProperties( with[ j ], knowledgeBase ); extraMasks[ j ] = PropertySpecificUtil.getEmptyPropertyReactiveMask( inverseSettableProperties[ j ].size() ); } for ( int j = 0; j < with.length; j++ ) { task.computeModificationMasks( modificationMask, settableProperties, with, extraMasks, inverseSettableProperties ); } } else { task.computeModificationMasks( modificationMask, settableProperties, null, null, null ); } } protected List<String> getAccessibleProperties( Object o, InternalKnowledgeBase knowledgeBase ) { if ( knowledgeBase != null ) { return PropertySpecificUtil.getAccessibleProperties( knowledgeBase, o.getClass() ); } else { return ClassUtils.getAccessibleProperties( o.getClass() ); } } public BitMask getModificationMask() { return modificationMask; } public abstract Class getModificationClass(); protected <R,C> void addTask( MetaProperty<?,R,C> p, C val ) { addTask( p, val, Lit.SET ); } protected <R,C> void addTask( MetaProperty<?,R,C> p, C val, Lit mode ) { ModifyTaskLiteral<T,R,C> newTask = new ModifyTaskLiteral<T,R,C>( p, val, mode ); if ( task == null ) { task = newTask; } else { ModifyTaskLiteral<T,?,?> lastTask = task; while ( lastTask.nextTask != null ) { lastTask = lastTask.nextTask; } lastTask.nextTask = newTask; } } @Override public URI getUri() { if ( key == null ) { key = createURI(); } return key; } @Override public Object getId() { return getUri(); } protected URI createURI() { StringBuilder sb = new StringBuilder(); sb.append( MetadataContainer.getIdentifier( target ) ); sb.append( "/modify" ); ModifyTaskLiteral<T,?,?> t = task; while ( t != null ) { sb.append( "?" ).append( t.propertyLiteral.getName() ); t = t.nextTask; } return URI.create( sb.toString() ); } // not used for execution but for provenance logging only public <S,T> Modify<S> getInverse( T value ) { ModifyLiteral inverse = new InverseModifyLiteral( value ); ModifyTaskLiteral task = this.task; do { if ( isAffected( value, task.value ) ) { MetaProperty inv = ( (InvertibleMetaProperty) task.getProperty() ).getInverse(); inverse.addTask( inv, inv.isManyValued() ? Collections.singleton( getTarget() ) : getTarget(), task.mode == Lit.REMOVE ? Lit.REMOVE : Lit.ADD ) ; } task = task.nextTask; } while ( task != null ); return inverse; } protected boolean isAffected( Object value, Object taskValue ) { if ( value == taskValue ) { return true; } if ( taskValue instanceof Collection ) { Collection coll = (Collection) taskValue; for ( Object o : coll ) { if ( o == value ) { return true; } else if ( o instanceof TraitProxy ) { if ( value == ((TraitProxy) o).getObject() ) { return true; } } } } return false; } public static class ModifyTaskLiteral<T,R,C> implements ModifyTask, Serializable { protected MetaProperty<T,R,C> propertyLiteral; protected C value; protected Lit mode; protected ModifyTaskLiteral<T,?,?> nextTask; protected ModifyTaskLiteral( MetaProperty<?,R,C> p, C val, Lit mode ) { this.propertyLiteral = (MetaProperty<T,R,C>) p; this.mode = mode; this.value = val; } public void call( T target ) { if ( propertyLiteral.isManyValued() ) { propertyLiteral.asManyValuedProperty().set( target, (List<R>) value, mode ); } else { propertyLiteral.asFunctionalProperty().set( target, value, Lit.SET ); } if ( nextTask != null ) { nextTask.call( target ); } } public void computeModificationMasks( BitMask mask, List<String> settableProperties, Object[] with, BitMask[] extraMasks, List<String>[] inverseSettableProperties ) { if ( nextTask != null ) { nextTask.computeModificationMasks( mask, settableProperties, with, extraMasks, inverseSettableProperties ); } setPropertyOnMask( mask, settableProperties, propertyLiteral.getName() ); if ( with != null ) { for ( int j = 0; j < with.length; j++ ) { if ( value == with[ j ] && propertyLiteral instanceof InvertibleMetaProperty ) { setPropertyOnMask( extraMasks[ j ], inverseSettableProperties[ j ], ( (InvertibleMetaProperty) propertyLiteral ).getInverse().getName() ); } } } } @Override public boolean equals( Object o ) { if ( this == o ) return true; if ( o == null || getClass() != o.getClass() ) return false; ModifyTaskLiteral that = (ModifyTaskLiteral) o; if ( nextTask != null ? !nextTask.equals( that.nextTask ) : that.nextTask != null ) return false; if ( !propertyLiteral.equals( that.propertyLiteral ) ) return false; return true; } @Override public int hashCode() { int result = propertyLiteral.hashCode(); result = 31 * result + ( nextTask != null ? nextTask.hashCode() : 0 ); return result; } @Override public MetaProperty getProperty() { return propertyLiteral; } @Override public Object getValue() { return value; } @Override public Lit getMode() { return mode; } public ModifyTask getNext() { return nextTask; } } public static class InverseModifyLiteral extends ModifyLiteral { private static With[] nargs = new With[0]; public <X> InverseModifyLiteral( X range ) { super( range, nargs ); } @Override protected MetaClass getMetaClassInfo() { return null; } @Override public Class getModificationClass() { return getTarget().getClass(); } @Override public Object call() { throw new UnsupportedOperationException( ); } } }