Package org.drools.util

Source Code of org.drools.util.HierarchyEncoderImpl

/*
* Copyright 2013 JBoss Inc
*
* 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.util;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
* Encodes a hierachy using bit masks, according to the algorithm described in
* M.F. van Bommel, P. Wang, Encoding Multiple Inheritance Hierarchies for Lattice Operations
* Data & Knowledge Enfineering 50 (2004) 175-194
*
* @param <T>
*/
public class HierarchyEncoderImpl<T> extends CodedHierarchyImpl<T> implements HierarchyEncoder<T>, Externalizable {


    private ImmutableBitSet bottom = new ImmutableBitSet();

    public BitSet getBottom() {
        return bottom;
    }

    @Override
    public void writeExternal( ObjectOutput objectOutput ) throws IOException {
        super.writeExternal( objectOutput );
        objectOutput.writeObject( bottom );
    }

    @Override
    public void readExternal( ObjectInput objectInput ) throws IOException, ClassNotFoundException {
        super.readExternal( objectInput );
        bottom = (ImmutableBitSet) objectInput.readObject();
    }

    public BitSet encode( T member, Collection<T> parents ) {
        BitSet existing = getCode( member );
        if ( existing != null ) {
            return existing;
        }

        HierNode<T> node = new HierNode<T>( member );

        Set<HierNode<T>> parentNodes = floor( parents );

        for ( HierNode<T> parentNode : parentNodes ) {
            node.addParent( parentNode );
            parentNode.addChild( node );
        }
        encode( node );

        add( node );

        return node.getBitMask();
    }


    @Override
    protected void add( HierNode<T> node ) {
        super.add( node );
        bottom.merge( node.getBitMask() );
    }

    public void clear() {
        super.clear();
        bottom = new ImmutableBitSet();
    }



    // Debug only
    List<T> ancestorValues( T name ) {
        List<T> anx = new ArrayList<T>();
        Set<HierNode<T>> nodes = ancestorNodes( getNode(name)  );
        for ( HierNode<T> node : nodes ) {
            anx.add( node.getValue() );
        }
        return anx;
    }



    protected void encode( HierNode<T> node ) {
        Collection<HierNode<T>> parents = node.getParents();
        //System.out.println( "Trying to encode " + node );
        switch ( parents.size() ) {
            case 0 :
                BitSet zero = new BitSet();

                if ( hasKey(zero) ) {
                    HierNode root = getNodeByKey(zero);

                    if ( root.getValue() != null ) {
                        fixedRoot = true;
                        HierNode previousRoot = root;
                        root = new HierNode( (Object) null );
                        root.addChild( previousRoot );
                        previousRoot.addParent( root );

                        root.setBitMask( zero );

                        propagate( previousRoot, freeBit( root ) );
                        add( root );
                    }

                    node.addParent( root );
                    updateMask( node, increment( root.getBitMask(), freeBit( root ) ) );

                } else {
                    updateMask( node, new BitSet() );
                }
                break;
            case 1 :
                HierNode<T> parent = parents.iterator().next();
                updateMask( node, increment( parent.getBitMask(), freeBit( parent ) ) );
                break;
            default:
                inheritMerged( node );
                //System.out.println( " ----------------------------------------------------------------------------------------- " );
                resolveConflicts( node );
                break;
        }
    }


    protected void resolveConflicts( HierNode<T> x ) {
        boolean conflicted = false;
        Collection<HierNode<T>> nodes = new ArrayList<HierNode<T>>( getNodes() );
        for ( HierNode<T> y : nodes ) {
            if ( incomparable( x, y ) ) {
//                System.out.println( " \t\tIncomparability between " + x + " and " + y );
                int sup = superset( y, x );
                if ( sup == 0 ) {
                    //System.out.println( " \t\tIncomparable, with same mask " + y );
                    // can't use update mask here, or the already existing node would be removed
                    x.setBitMask( increment( x.getBitMask(), freeBit( x ) ) );
                    propagate( y, freeBit( x, y ) );
                }
                if ( sup > 0 ) {
//                    System.out.println( " \t\tIncomparable, but as parent " + y );
                    updateMask( x, increment( x.getBitMask(), freeBit( x ) ) );
                }
                int sub = superset( x, y );
                if ( sub > 0 ) {
//                    System.out.println( " \t\tIncomparable, but as child " + y );
                    modify( x, y );
                    conflicted = true;
                }

            }
        }
        if ( conflicted ) {
            inheritMerged( x );
            resolveConflicts( x );
        }

    }

    protected void modify( HierNode<T> x, HierNode<T> y ) {
        //System.out.println( "Modifying on a inc between " + x + " and " + y );

        int i = freeBit( x, y );
        //System.out.println( "I " + i );

        //System.out.println( "Getting parents of " + y + " >> " + y.getParents() );
        Collection<HierNode<T>> py = y.getParents();
        BitSet t = new BitSet( y.getBitMask().length() );
        for ( HierNode<T> parent : py ) {
            t.or( parent.getBitMask() );
        }

        BitSet d = singleBitDiff( t, y.getBitMask() );
        int inDex = d.nextSetBit( 0 );

        if ( inDex < 0 ) {
            propagate( y, i );
        } else {

            //System.out.println( "D " + toBinaryString( d ) );


            Set<HierNode<T>> ancestors = ancestorNodes( x );
            Set<HierNode<T>> affectedAncestors = new HashSet<HierNode<T>>();

            for ( HierNode<T> anc : ancestors ) {
                if ( anc.getBitMask().get( inDex ) ) {
                    affectedAncestors.add( anc );
                }
            }
            //System.out.println( "Ancestors of " + x + " >> " + ancestors );
            //System.out.println( "Affected " + x + " >> " + affectedAncestors );

            if ( affectedAncestors.size() == 0 ) {
                return;
            }

            Set<HierNode<T>> gcs = gcs( affectedAncestors );
            //System.out.println( "GCS of Affected " + gcs );

            Set<HierNode<T>> affectedDescendants = new HashSet<HierNode<T>>();
            for ( HierNode<T> g : gcs ) {
                affectedDescendants.addAll( descendantNodes( g ) );
            }
            affectedDescendants.remove( x );    // take x out it's not yet in the set

            int dx = firstOne( d );

            if ( bottom.get( i ) ) {
                i = freeBit( new HierNode<T>( bottom ) );
            }

            for ( HierNode<T> sub : affectedDescendants ) {

                boolean keepsBit = false;
                for ( HierNode<T> sup : sub.getParents() ) {
                    if ( ! keepsBit && ! affectedDescendants.contains( sup ) && sup.getBitMask().get( inDex ) ) {
                        keepsBit = true;
                    }
                }
                BitSet subMask = sub.getBitMask();
                if ( ! keepsBit ) {
                    subMask = decrement( subMask, dx );
                }
                subMask = increment( subMask, i );

                updateMask( sub, subMask );

                //System.out.println( "\tModified Desc" + sub );
            }

            inDex = d.nextSetBit( inDex + 1 );
        }


    }

    protected void updateMask( HierNode<T> node, BitSet mask ) {
        boolean in = node.getBitMask() != null && contains ( node );
        if ( in ) { remove( node ); }
        node.setBitMask( mask );
        if ( in ) { add( node ); }
    }

    protected void inheritMerged( HierNode<T> x ) {
        BitSet mask = new BitSet( x.getBitMask() != null ? x.getBitMask().length() : 1 );
        for ( HierNode<T> p : x.getParents() ) {
            mask.or(p.getBitMask());
        }
        updateMask(x, mask);
    }


    protected Set<HierNode<T>> gcs( Set<HierNode<T>> set ) {
        Set<HierNode<T>> s = new HashSet<HierNode<T>>();

        Iterator<HierNode<T>> iter = set.iterator();
        BitSet a = new BitSet( this.size() );
        a.or( iter.next().getBitMask() );
        while ( iter.hasNext() ) {
            a.and( iter.next().getBitMask() );
        }
        //System.out.println( "Root mask for ceil " + toBinaryString( a ) );
        for ( HierNode<T> node : getNodes() ) {
            if ( superset( node.getBitMask(), a ) >= 0 ) {
                s.add( node );
            }
        }

        Set<HierNode<T>> cl = ceil( s );
        return cl;
    }

    protected Set<HierNode<T>> ceil( Set<HierNode<T>> s ) {
        //System.out.println( "Looking for the ceiling of " + s);
        if ( s.size() <= 1 ) { return s; }
        Set<HierNode<T>> ceil = new HashSet<HierNode<T>>( s );
        for ( HierNode<T> x : s ) {
            for( HierNode<T> y : s ) {
                if ( superset( x, y ) > 0 ) {
                    ceil.remove( x );
                    break;
                }
            }
        }
        //System.out.println("Found ceiling " + ceil);
        return ceil;
    }

    protected Set<HierNode<T>> floor( Set<HierNode<T>> s ) {
        //System.out.println( "Looking for the floor of " + s);
        if ( s.size() <= 1 ) { return s; }
        Set<HierNode<T>> ceil = new HashSet<HierNode<T>>( s );
        for ( HierNode<T> x : s ) {
            for( HierNode<T> y : s ) {
                if ( superset( y, x ) > 0 ) {
                    ceil.remove( x );
                    break;
                }
            }
        }
        //System.out.println("Found ceiling " + ceil);
        return ceil;
    }

    private Set<HierNode<T>> floor( Collection<T> parents ) {
        Set<HierNode<T>> floor = new HashSet();
        Set<HierNode<T>> subs = new HashSet();

        for ( T s : parents ) {
            HierNode<T> x = getNode( s );
            subs.addAll( floor );

            boolean minimal = true;
            for ( HierNode<T> y : subs ) {
                if ( superset( x, y ) > 0 ) {
                    floor.remove( y );
                }
                if ( superset( y, x ) > 0 ) {
                    minimal = false;
                    break;
                }
            }
            if ( minimal ) {
                floor.add( x );
            }

            subs.clear();
        }
        return floor;
    }


    protected void propagate( HierNode<T> y, int bit ) {
        Set<HierNode<T>> descendants = descendantNodes(y);
        for ( HierNode<T> s : descendants ) {
            updateMask( s, increment( s.getBitMask(), bit ) );
        }
    }


    protected int freeBit( HierNode<T> x ) {
        return freeBit( x, null );
    }

    protected int freeBit( HierNode<T> x, HierNode<T> z ) {
        //System.out.println( "Looking for a free bit in node " + x );
        BitSet forbid = new BitSet( this.size() );
        forbid.or( x.getBitMask() );
        for ( HierNode<T> y : getNodes() ) {

            if ( superset( y, x ) > 0 ) {
                //System.out.println( "\t\t Subtype " + y + " contributes " + toBinaryString( y.getBitMask() ) );
                forbid.or( y.getBitMask() );
            }

            if ( z != null ) {
                if ( superset( y, z ) > 0 ) {
//                  System.out.println( "\t\t Subtype " + y + " contributes " + toBinaryString( y.getBitMask() ) );
                    forbid.or( y.getBitMask() );
                }
            }

            if ( superset( x, y ) < 0 ) {
                BitSet diff = singleBitDiff( x.getBitMask(), y.getBitMask() );
                //System.out.println( "\t\t Incomparable " + y + " contributes " + toBinaryString( diff ) );
                forbid.or(diff);
            }
        }
        //System.out.println( "\t Forbidden mask " + toBinaryString( forbid ) );
        return firstZero( forbid );
    }






    protected boolean incomparable( HierNode<T> c1, HierNode<T> c2 ) {
        if ( c1 == c2 ) { return false; }
        Set<HierNode<T>> sup1 = ancestorNodes( c1 );
        Set<HierNode<T>> sup2 = ancestorNodes( c2 );
        return ! ( sup1.contains( c2 ) || sup2.contains( c1 ) );
    }















    BitSet increment( BitSet id, int i ) {
        BitSet x = new BitSet( Math.max( i + 1, id.length() ) );
        x.or( id );
        x.set(i);
        return x;
    }

    BitSet decrement( BitSet id, int d ) {
        BitSet x = new BitSet( id.length() );
        x.or( id );
        x.clear(d);
        return x;
    }

    BitSet singleBitDiff( BitSet x, BitSet y ) {
        BitSet t = new BitSet( x.length() );
        t.or( x );
        t.flip(0, t.size());
        t.and( y );

        switch ( t.cardinality() ) {
            case 0 : return t;
            case 1 : return t;
            default: return new BitSet();
        }
    }

    int firstOne( BitSet t ) {
        return t.nextSetBit(0);
    }

    int firstZero( BitSet t ) {
        return t.nextClearBit(0);
    }


    private class ImmutableBitSet extends BitSet {

        @Override
        public void flip(int bitIndex) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void flip(int fromIndex, int toIndex) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void set(int bitIndex) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void set(int bitIndex, boolean value) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void set(int fromIndex, int toIndex) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void set(int fromIndex, int toIndex, boolean value) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void clear(int bitIndex) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void clear(int fromIndex, int toIndex) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void clear() { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void and(BitSet set) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void or(BitSet set) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void xor(BitSet set) { throw new UnsupportedOperationException( "Read Only" ); }

        @Override
        public void andNot(BitSet set) { throw new UnsupportedOperationException( "Read Only" ); }

        protected void merge( BitSet set ) {
            super.or( set );
        }
    }


}
TOP

Related Classes of org.drools.util.HierarchyEncoderImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.