All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.objectstyle.cayenne.util.IndexPropertyList Maven / Gradle / Ivy

/* ====================================================================
 * 
 * The ObjectStyle Group Software License, version 1.1
 * ObjectStyle Group - http://objectstyle.org/
 * 
 * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
 * of the software. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The end-user documentation included with the redistribution, if any,
 *    must include the following acknowlegement:
 *    "This product includes software developed by independent contributors
 *    and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 * 
 * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
 *    or promote products derived from this software without prior written
 *    permission. For written permission, email
 *    "andrus at objectstyle dot org".
 * 
 * 5. Products derived from this software may not be called "ObjectStyle"
 *    or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
 *    names without prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by many
 * individuals and hosted on ObjectStyle Group web site.  For more
 * information on the ObjectStyle Group, please see
 * .
 */
package org.objectstyle.cayenne.util;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.objectstyle.cayenne.CayenneRuntimeException;
import org.objectstyle.cayenne.ValueHolder;
import org.objectstyle.cayenne.property.PropertyUtils;

/**
 * A List implementation that would maintain its internal ordering based on some object
 * numeric "index" property. When objects are added to the list at a certain index, an
 * "index" property is modified to reflect list order, when objects are removed, their
 * index property is set to the negative number.
 * 

* For performance reasons this implementation does not guarantee that there is no gaps in * the integer ordering sequence (i.e. generally * object.getIndexProperty() != list.indexOf(object)). However it * guarantees the right ordering based on index property. * * @since 1.2 * @author Andrus Adamchik */ public class IndexPropertyList extends AbstractList implements ValueHolder { /** * A default gap maintained between elements index property values. Gaps bigger than 1 * ensure faster and less intrusive additions and removals. */ static final int DEFAULT_GAP = 3; /** * A list used for the actual objects storage. */ protected List list; protected String indexProperty; boolean dirty; Comparator comparator; /** * Creates an empty NumericPropertyOrderedList. */ public IndexPropertyList(String indexProperty) { this(indexProperty, new ArrayList(), false); } /** * Creates a NumericPropertyOrderedList that decorates another list. If the list is * not known to be properly sorted, caller must set sortNeeded to true. * This will result in sorting the original list on first access attempt. */ public IndexPropertyList(String indexProperty, List objects, boolean sortNeeded) { if (indexProperty == null) { throw new IllegalArgumentException("Null sortProperty"); } if (objects == null) { throw new IllegalArgumentException("Null objects list."); } this.indexProperty = indexProperty; this.list = objects; // be lazy - don't sort here, as (a) it may never be needed and (b) a list can be // a Cayenne fault, so resolving it too early is undesirable this.dirty = sortNeeded; } ValueHolder getWrappedValueHolder() { return (list instanceof ValueHolder) ? (ValueHolder) list : null; } public boolean isFault() { ValueHolder h = getWrappedValueHolder(); return (h != null) ? h.isFault() : false; } public Object setValueDirectly(Object value) throws CayenneRuntimeException { ValueHolder h = getWrappedValueHolder(); return h != null ? h.setValueDirectly(value) : null; } public Object setValue(Object value) throws CayenneRuntimeException { ValueHolder h = getWrappedValueHolder(); return h != null ? h.setValue(value) : null; } public Object getValue() throws CayenneRuntimeException { ValueHolder h = getWrappedValueHolder(); return h != null ? h.getValue() : null; } public Object getValueDirectly() throws CayenneRuntimeException { ValueHolder h = getWrappedValueHolder(); return h != null ? h.getValueDirectly() : null; } public void invalidate() { ValueHolder h = getWrappedValueHolder(); if (h != null) { h.invalidate(); } } /** * Changes list state to "dirty" forcing reordering on next access. */ public void touch() { this.dirty = true; } public Object get(int index) { if (dirty) { sort(); } return list.get(index); } public int size() { if (dirty) { sort(); } return list.size(); } public Object set(int index, Object element) { if (dirty) { sort(); } Object removed = list.set(index, element); if (element != null) { int indexValue = (removed != null) ? getIndexValue(removed) : calculateIndexValue(index); setIndexValue(element, indexValue); shift(index + 1, indexValue); } if (removed != null && removed != element) { setIndexValue(removed, -1); } return removed; } public void add(int index, Object element) { if (dirty) { sort(); } list.add(index, element); if (element != null) { int indexValue = calculateIndexValue(index); setIndexValue(element, indexValue); shift(index + 1, indexValue); } } public Object remove(int index) { if (dirty) { sort(); } Object removed = list.remove(index); if (removed != null) { setIndexValue(removed, -1); } return removed; } // ============================================ // ***** Methods to maintain ordering ****** // ============================================ /** * Calculates an index value at the specified list index. Note that using this value * may require a shift of the objects following this index. */ protected int calculateIndexValue(int listIndex) { if (list.isEmpty()) { throw new ArrayIndexOutOfBoundsException(listIndex); } if (list.size() == 1 && listIndex == 0) { return 0; } // handle lists with teo or more elements... // last element if (listIndex == list.size() - 1) { return getIndexValue(get(listIndex - 1)) + DEFAULT_GAP; } int from = (listIndex == 0) ? -1 : getIndexValue(get(listIndex - 1)); int to = getIndexValue(get(listIndex + 1)); return (to - from > 1) ? (to - from) / 2 + from : from + DEFAULT_GAP; } protected int getIndexValue(Object object) { Number n = (Number) PropertyUtils.getProperty(object, indexProperty); if (n == null) { throw new CayenneRuntimeException("Null index property '" + indexProperty + "' for object " + object); } return n.intValue(); } protected void setIndexValue(Object object, int index) { PropertyUtils.setProperty(object, indexProperty, new Integer(index)); } protected void shift(int startIndex, int afterIndexValue) { int size = size(); for (int i = startIndex; i < size; i++) { Object object = get(i); int indexValue = getIndexValue(object); if (indexValue > afterIndexValue) { break; } int newValue = calculateIndexValue(i); setIndexValue(object, newValue); afterIndexValue = newValue; } } /** * Sorts internal list. */ protected void sort() { if (!dirty) { return; } // do not directly sort Cayenne lists, sort the underlying list instead to avoid a // bunch of additions/removals Collections.sort(unwrapList(), getComparator()); dirty = false; } List unwrapList() { if (list instanceof PersistentObjectList) { return ((PersistentObjectList) list).resolvedObjectList(); } else { return list; } } /** * Returns a property Comaparator, creating it on demand. */ Comparator getComparator() { if (comparator == null) { comparator = new Comparator() { public int compare(Object o1, Object o2) { if ((o1 == null && o2 == null) || o1 == o2) { return 0; } else if (o1 == null && o2 != null) { return -1; } else if (o1 != null && o2 == null) { return 1; } Comparable p1 = (Comparable) PropertyUtils.getProperty( o1, indexProperty); Comparable p2 = (Comparable) PropertyUtils.getProperty( o2, indexProperty); return (p1 == null) ? -1 : p1.compareTo(p2); } }; } return comparator; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy