components/visual-panels/core/src/java/vpanels/panel/com/oracle/solaris/vp/panel/common/model/FilterManagedObject.java
changeset 3553 f1d133b09a8c
parent 3552 077ebe3d0d24
child 3554 ef58713bafc4
equal deleted inserted replaced
3552:077ebe3d0d24 3553:f1d133b09a8c
     1 /*
       
     2  * CDDL HEADER START
       
     3  *
       
     4  * The contents of this file are subject to the terms of the
       
     5  * Common Development and Distribution License (the "License").
       
     6  * You may not use this file except in compliance with the License.
       
     7  *
       
     8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
       
     9  * or http://www.opensolaris.org/os/licensing.
       
    10  * See the License for the specific language governing permissions
       
    11  * and limitations under the License.
       
    12  *
       
    13  * When distributing Covered Code, include this CDDL HEADER in each
       
    14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
       
    15  * If applicable, add the following below this CDDL HEADER, with the
       
    16  * fields enclosed by brackets "[]" replaced with your own identifying
       
    17  * information: Portions Copyright [yyyy] [name of copyright owner]
       
    18  *
       
    19  * CDDL HEADER END
       
    20  */
       
    21 
       
    22 /*
       
    23  * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
       
    24  */
       
    25 
       
    26 package com.oracle.solaris.vp.panel.common.model;
       
    27 
       
    28 import java.beans.*;
       
    29 import java.util.*;
       
    30 import com.oracle.solaris.vp.util.misc.event.*;
       
    31 import com.oracle.solaris.vp.util.misc.predicate.*;
       
    32 
       
    33 /**
       
    34  * Generic {@code ManagedObject} implementation that filters the children of
       
    35  * a {@code ManagedObject}.  Only those children that satisfy a provided
       
    36  * {@code Predicate} appear to be children of the {@code FilterManagedObject}.
       
    37  */
       
    38 public class FilterManagedObject<T extends ManagedObject>
       
    39     extends ManagedObjectMonitor<T>
       
    40     implements ManagedObject<T>, PropertyChangeListener
       
    41 {
       
    42     private Predicate<? super T> pred_;	/** The selection predicate */
       
    43 
       
    44     private IntervalListeners iListeners_ = new IntervalListeners();
       
    45 
       
    46     /*
       
    47      * These four are protected by synchronizing on "children_"
       
    48      */
       
    49     private List<Integer> childMap_ = new ArrayList<Integer>();
       
    50     private final List<T> children_ = new ArrayList<T>();
       
    51     private List<T> roChildren_ = Collections.unmodifiableList(children_);
       
    52 
       
    53     /**
       
    54      * Constructs a FilterManagedObject.
       
    55      *
       
    56      * @param wrappee the {@code ManagedObject} whose children are to
       
    57      * be filtered
       
    58      * @param predicate the {@code Predicate} that determines which children
       
    59      * are seen
       
    60      */
       
    61     public FilterManagedObject(ManagedObject<T> wrappee,
       
    62 	Predicate<? super T> predicate)
       
    63     {
       
    64 	super(wrappee);
       
    65 
       
    66 	pred_ = predicate;
       
    67 
       
    68 	/*
       
    69 	 * Should probably be implemented in terms of intervalAdded(), like
       
    70 	 * setManagedObject() is.
       
    71 	 */
       
    72 	synchronized (getManagedObject().getChildrenLock()) {
       
    73 	    List<T> children = getManagedObject().getChildren();
       
    74 	    int loc = 0;
       
    75 	    for (T child : children) {
       
    76 		if (pred_.test(child)) {
       
    77 		    children_.add(child);
       
    78 		    childMap_.add(loc);
       
    79 		    loc++;
       
    80 		} else {
       
    81 		    childMap_.add(null);
       
    82 		}
       
    83 	    }
       
    84 
       
    85 	    getManagedObject().addPropertyChangeListener(this);
       
    86 	}
       
    87     }
       
    88 
       
    89     public FilterManagedObject(ManagedObject<T> wrappee) {
       
    90 	this(wrappee, new TruePredicate<T>());
       
    91     }
       
    92 
       
    93     /**
       
    94      * Replaces the predicate used by the FilterManagedObject.
       
    95      *
       
    96      * @param predicate the new {@code Predicate} to use
       
    97      */
       
    98     public void setPredicate(Predicate<? super T> predicate)
       
    99     {
       
   100 	IntervalEventQueue ieq = new IntervalEventQueue(this, iListeners_);
       
   101 
       
   102 	synchronized (getManagedObject().getChildrenLock()) {
       
   103 	    synchronized (children_) {
       
   104 		List<T> children = getManagedObject().getChildren();
       
   105 
       
   106 		int loc = 0;
       
   107 		int size = children.size();
       
   108 		for (int i = 0; i < size; i++) {
       
   109 		    T o = children.get(i);
       
   110 		    boolean matches = predicate.test(o);
       
   111 		    Integer mapping = childMap_.get(i);
       
   112 		    if (mapping != null) {
       
   113 			if (matches) {
       
   114 			    if (mapping != loc)
       
   115 				childMap_.set(i, loc);
       
   116 			    loc++;
       
   117 			} else {
       
   118 			    ieq.addIndex(loc, IntervalEventQueue.Type.REMOVE);
       
   119 			    children_.remove(loc);
       
   120 			    childMap_.set(i, null);
       
   121 			}
       
   122 		    } else {
       
   123 			if (matches) {
       
   124 			    ieq.addIndex(loc, IntervalEventQueue.Type.ADD);
       
   125 			    children_.add(loc, o);
       
   126 			    childMap_.set(i, loc);
       
   127 			    loc++;
       
   128 			} /* else do nothing */
       
   129 		    }
       
   130 		}
       
   131 		ieq.flush();
       
   132 		pred_ = predicate;
       
   133 	    }
       
   134 	}
       
   135     }
       
   136 
       
   137     /**
       
   138      * Refreshes the child view.  Used if the behavior of the
       
   139      * predicate changes.
       
   140      */
       
   141     public void refreshPredicate()
       
   142     {
       
   143 	setPredicate(pred_);
       
   144     }
       
   145 
       
   146     /**
       
   147      * Given the index of an entry to add to the inbound children list,
       
   148      * find the appropriate location in the outbound children list.
       
   149      *
       
   150      * @param index the index of the new inbound child
       
   151      * @return the index of the corresponding outbound child
       
   152      */
       
   153     private int findNewMapping(int index)
       
   154     {
       
   155 	int oldsize = childMap_.size();
       
   156 	int result;
       
   157 
       
   158 	assert (index <= oldsize);
       
   159 
       
   160 	if (oldsize == 0) {
       
   161 	    /* Adding to an empty list; the first index must be 0 */
       
   162 	    result = 0;
       
   163 	} else {
       
   164 	    Integer mapping;
       
   165 	    result = -1;
       
   166 
       
   167 	    /* Find the last non-null entry... */
       
   168 	    if (index > 0) {
       
   169 		for (int i = index - 1; i >= 0; i--)
       
   170 		    if ((mapping = childMap_.get(i)) != null) {
       
   171 			result = mapping + 1;
       
   172 			break;
       
   173 		    }
       
   174 	    }
       
   175 
       
   176 	    /* ...failing that, the next non-null entry. */
       
   177 	    if (result == -1 && index < oldsize) {
       
   178 		for (int i = index; i < oldsize; i++)
       
   179 		    if ((mapping = childMap_.get(i)) != null) {
       
   180 			result = mapping;
       
   181 			break;
       
   182 		    }
       
   183 	    }
       
   184 
       
   185 	    /* Our mapping is empty; start at the beginning. */
       
   186 	    if (result == -1)
       
   187 		result = 0;
       
   188 	}
       
   189 
       
   190 	return (result);
       
   191     }
       
   192 
       
   193     /**
       
   194      * Shifts all non-null mappings after a point by the specified amount.
       
   195      *
       
   196      * @param start the first index whose value should be adjusted
       
   197      * @param delta the amount by which values should be adjusted
       
   198      */
       
   199     private void adjustMappings(int start, int delta)
       
   200     {
       
   201 	int size = childMap_.size();
       
   202 	for (int i = start; i < size; i++) {
       
   203 	    Integer mapping = childMap_.get(i);
       
   204 	    if (mapping != null) {
       
   205 		assert (mapping + delta >= 0);
       
   206 		childMap_.set(i, mapping + delta);
       
   207 	    }
       
   208 	}
       
   209     }
       
   210 
       
   211     /*
       
   212      * ManagedObject methods
       
   213      */
       
   214 
       
   215     @Override
       
   216     public void dispose() {
       
   217     }
       
   218 
       
   219     @Override
       
   220     public String getId()
       
   221     {
       
   222 	return (getManagedObject().getId());
       
   223     }
       
   224 
       
   225     @Override
       
   226     public String getDescription()
       
   227     {
       
   228 	return (getManagedObject().getDescription());
       
   229     }
       
   230 
       
   231     @Override
       
   232     public List<T> getChildren()
       
   233     {
       
   234 	return (roChildren_);
       
   235     }
       
   236 
       
   237     @Override
       
   238     public Object getChildrenLock()
       
   239     {
       
   240 	return (children_);
       
   241     }
       
   242 
       
   243     @Override
       
   244     public String getName()
       
   245     {
       
   246 	return (getManagedObject().getName());
       
   247     }
       
   248 
       
   249     @Override
       
   250     public ManagedObjectStatus getStatus()
       
   251     {
       
   252 	return (getManagedObject().getStatus());
       
   253     }
       
   254 
       
   255     @Override
       
   256     public String getStatusText()
       
   257     {
       
   258 	return (getManagedObject().getStatusText());
       
   259     }
       
   260 
       
   261     @Override
       
   262     public void addIntervalListener(IntervalListener l)
       
   263     {
       
   264 	iListeners_.add(l);
       
   265     }
       
   266 
       
   267     @Override
       
   268     public boolean removeIntervalListener(IntervalListener l)
       
   269     {
       
   270 	return (iListeners_.remove(l));
       
   271     }
       
   272 
       
   273     /*
       
   274      * PropertyChangeListener methods
       
   275      */
       
   276 
       
   277     @Override
       
   278     public void propertyChange(PropertyChangeEvent evt)
       
   279     {
       
   280 	Object source = evt.getSource();
       
   281 
       
   282 	/* Throw away events from children we aren't exposing. */
       
   283 	synchronized (children_) {
       
   284 	    if (listenees_.contains(source) && !children_.contains(source))
       
   285 		return;
       
   286 
       
   287 	    pListeners_.propertyChange(evt);
       
   288 	}
       
   289     }
       
   290 
       
   291     /*
       
   292      * IntervalListener methods
       
   293      */
       
   294 
       
   295     @Override
       
   296     public void intervalAdded(IntervalEvent e)
       
   297     {
       
   298 	/* synchronized (getManagedObject().getChildrenLock()) implied */
       
   299 	synchronized (children_) {
       
   300 	    super.intervalAdded(e);
       
   301 
       
   302 	    int f = e.getFirstIndex();
       
   303 	    int l = e.getLastIndex();
       
   304 	    int first = findNewMapping(f);
       
   305 	    int last = first;
       
   306 	    int i;
       
   307 
       
   308 	    List<T> adds = listenees_.subList(f, l + 1);
       
   309 	    i = f;
       
   310 	    for (T o : adds) {
       
   311 		if (pred_.test(o)) {
       
   312 		    childMap_.add(i, last);
       
   313 		    children_.add(last, o);
       
   314 		    last++;
       
   315 		} else {
       
   316 		    childMap_.add(i, null);
       
   317 		}
       
   318 		i++;
       
   319 	    }
       
   320 
       
   321 	    if (--last >= first) {
       
   322 		adjustMappings(l + 1, last - first + 1);
       
   323 		iListeners_.intervalAdded(new IntervalEvent(this, first, last));
       
   324 	    }
       
   325 	}
       
   326     }
       
   327 
       
   328     @Override
       
   329     public void intervalRemoved(IntervalEvent e)
       
   330     {
       
   331 	/* synchronized (inner_.getChildrenLock()) implied */
       
   332 	synchronized (children_) {
       
   333 	    int f = e.getFirstIndex();
       
   334 	    int l = e.getLastIndex();
       
   335 
       
   336 	    super.intervalRemoved(e);
       
   337 
       
   338 	    /* Find mapping in codomain, remove from domain */
       
   339 	    int first = children_.size();
       
   340 	    int last = -1;
       
   341 	    for (int i = f; i <= l; i++) {
       
   342 		Integer mapping = childMap_.get(f);
       
   343 		childMap_.remove(f);
       
   344 		if (mapping != null) {
       
   345 		    if (mapping < first)
       
   346 			first = mapping;
       
   347 		    if (mapping > last)
       
   348 			last = mapping;
       
   349 		}
       
   350 	    }
       
   351 
       
   352 	    /* Remove from codomain */
       
   353 	    children_.subList(first, last + 1).clear();
       
   354 
       
   355 	    if (first <= last) {
       
   356 		adjustMappings(f, - (last - first + 1));
       
   357 		iListeners_.intervalRemoved(
       
   358 		    new IntervalEvent(this, first, last));
       
   359 	    }
       
   360 	}
       
   361     }
       
   362 
       
   363     //
       
   364     // ManagedObjectMonitor methods
       
   365     //
       
   366 
       
   367     @Override
       
   368     public void setManagedObject(ManagedObject<T> mo) {
       
   369 	synchronized (this) {
       
   370 	    getManagedObject().removePropertyChangeListener(this);
       
   371 	    super.setManagedObject(mo);
       
   372 	    mo.addPropertyChangeListener(this);
       
   373 	}
       
   374     }
       
   375 
       
   376     /**
       
   377      * Called by the PropertyChangeListener registered with inbound
       
   378      * children.  Makes appropriate change to outbound children list.
       
   379      */
       
   380     @Override
       
   381     protected void managedObjectPropertyChange(T child, PropertyChangeEvent evt)
       
   382     {
       
   383 	synchronized (children_) {
       
   384 	    int index = listenees_.indexOf(child);
       
   385 	    boolean match = pred_.test(child);
       
   386 	    Integer mapping = childMap_.get(index);
       
   387 	    if (match && mapping == null) {
       
   388 		/* Needs to be added to exported model */
       
   389 		mapping = findNewMapping(index);
       
   390 		children_.add(mapping, child);
       
   391 		childMap_.set(index, mapping);
       
   392 		adjustMappings(index + 1, 1);
       
   393 		iListeners_.intervalAdded(
       
   394 		    new IntervalEvent(this, mapping, mapping));
       
   395 	    } else if (!match && mapping != null) {
       
   396 		/* Needs to be removed from exported model */
       
   397 		children_.remove(mapping.intValue());
       
   398 		childMap_.set(index, null);
       
   399 		adjustMappings(index + 1, -1);
       
   400 		iListeners_.intervalRemoved(
       
   401 		    new IntervalEvent(this, mapping, mapping));
       
   402 	    }
       
   403 	}
       
   404     }
       
   405 }