/* * Copyright (C) 2004 The Concord Consortium, Inc., * 10 Concord Crossing, Concord, MA 01742 * * Web Site: http://www.concord.org * Email: info@concord.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * END LICENSE */ /* * Last modification information: * $Revision: 1.16 $ * $Date: 2007-10-10 03:09:05 $ * $Author: scytacki $ * * Licence Information * Copyright 2004 The Concord Consortium */ package org.concord.otrunk; import java.lang.reflect.Array; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; import org.concord.framework.otrunk.OTChangeEvent; import org.concord.framework.otrunk.OTID; import org.concord.framework.otrunk.OTObject; import org.concord.framework.otrunk.OTObjectList; import org.concord.otrunk.datamodel.OTDataList; /** * OTObjectList * Class name and description * * Date created: Nov 9, 2004 * * @author scott<p> * */ public class OTObjectListImpl extends OTCollectionImpl implements OTObjectList { protected OTDataList list; /** * This is used to store references to the OTObjects, this prevents them from * getting garbage collected as long as this collection is referenced. <p> * * Using an ArrayList seemed like the natural thing to do, but it is hard to keep * that synchronized with the data list. So instead a map is used. */ protected HashMap<OTID, OTObject> referenceMap; public OTObjectListImpl(String property, OTDataList resList, OTObjectInternal objectInternal) { super(property, objectInternal); this.list = resList; } protected OTID getId(int index) { if (index < 0){ return null; } OTID id = (OTID)list.get(index); if(id == null) { System.err.println("Null item in object list: \n" + " " + objectInternal.getGlobalId() + "." + property + "[" + index + "]"); return null; } return id; } public OTObject get(int index) { try { OTID id = getId(index); if(id == null) { return null; } OTObject otObject = objectInternal.getOTObject(id); if(referenceMap == null){ referenceMap = new HashMap<OTID, OTObject>(); } referenceMap.put(id, otObject); return otObject; } catch (Exception e) { e.printStackTrace(); } return null; } /** * * @see org.concord.framework.otrunk.OTObjectList#getVector() */ public Vector<OTObject> getVector() { Vector<OTObject> childVector = new Vector<OTObject>(); for(int i=0; i<list.size(); i++) { try { OTID childID = getId(i); if(childID == null){ childVector.add(null); } else { childVector.add(objectInternal.getOTObject(childID)); } } catch (Exception e) { e.printStackTrace(); } } return childVector; } public boolean add(OTObject obj) { OTID id = obj.getGlobalId(); if(id == null) { throw new RuntimeException("adding null id object list"); } list.add(id); if(referenceMap == null){ referenceMap = new HashMap<OTID, OTObject>(); } referenceMap.put(id, obj); notifyOTChange(OTChangeEvent.OP_ADD, obj, null); return true; } public void add(int index, OTObject obj) { OTID id = obj.getGlobalId(); if(id == null) { throw new RuntimeException("adding null id object list"); } list.add(index, id); if(referenceMap == null){ referenceMap = new HashMap<OTID, OTObject>(); } referenceMap.put(id, obj); notifyOTChange(OTChangeEvent.OP_ADD, obj, null); } /* * This is a hack until we can sort this out * it would be best if the users of this list could have all ids hidden from * them. */ public void add(OTID id) { list.add(id); // FIXME will screw up some listeners which expect an object not an // id. But the reason this call is here is for efficiency so the actual // OTObject doesn't need to be created. So it isn't clear what to do notifyOTChange(OTChangeEvent.OP_ADD, id, null); } public void set(int index, OTObject obj) { OTID id = obj.getGlobalId(); if(id == null) { throw new RuntimeException("adding null id object list"); } Object previousObject = list.set(index, id); if(previousObject instanceof OTID){ try { previousObject = objectInternal.getOTObject((OTID) previousObject); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(referenceMap == null){ referenceMap = new HashMap<OTID, OTObject>(); } referenceMap.put(id, obj); if(previousObject != null){ // FIXME we should remove the reference from this list only if it hasn't // be set into 2 different places. We'd need to track where each object // was inserted to do this correctly. } notifyOTChange(OTChangeEvent.OP_SET, obj, previousObject); } public int size() { return list.size(); } public void clear() { list.removeAll(); referenceMap = null; notifyOTChange(OTChangeEvent.OP_REMOVE_ALL, null, null); } /** * @see org.concord.framework.otrunk.OTObjectList#remove(org.concord.framework.otrunk.OTObject) */ public boolean remove(Object genericObj) { if(!(genericObj instanceof OTObject)){ throw new RuntimeException("can't remove object that isn't a OTObject"); } OTObject obj = (OTObject) genericObj; OTID id = obj.getGlobalId(); if(id == null) { throw new RuntimeException("adding null id object list"); } list.remove(id); if(referenceMap != null){ referenceMap.put(id, null); } notifyOTChange(OTChangeEvent.OP_REMOVE, obj, null); return true; } /** * @see org.concord.framework.otrunk.OTObjectList#remove(int) */ public void remove(int index) { OTID id = getId(index); list.remove(index); OTObject obj = null; if(id != null){ try { obj = objectInternal.getOTObject(id); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } if(referenceMap != null){ referenceMap.put(id, null); } } notifyOTChange(OTChangeEvent.OP_REMOVE, obj, null); } /** * This is package protected. It should not be used outside of this package, * because it will be removed at some point. * * @return */ OTDataList getDataList() { return list; } public Iterator<OTObject> iterator() { return new Iterator<OTObject>(){ /** * This points to the current object index; */ int index = -1; public boolean hasNext() { return index < (size() - 1); } public OTObject next() { index++; return get(index); } public void remove() { OTObjectListImpl.this.remove(index); } }; } public boolean contains(Object obj) { if(!(obj instanceof OTObject)){ throw new IllegalArgumentException("not an OTObject"); } OTID id = ((OTObject)obj).getGlobalId(); if(id == null) { throw new IllegalArgumentException("null object id"); } return list.contains(id); } public Object[] toArray() { return toArray(new OTObject[list.size()]); } @SuppressWarnings("unchecked") public <T> T[] toArray(T[] array) { int size = list.size(); Class<?> componentType = array.getClass().getComponentType(); if(array.length < size){ array = (T[])Array.newInstance(componentType, size); } Object objToBeStored = null; for(int i=0; i<size; i++) { try { OTID childID = getId(i); if(childID == null){ array[i] = null; } else { objToBeStored = objectInternal.getOTObject(childID); array[i] = (T) objToBeStored; } } catch (ArrayStoreException ase) { throw new ArrayStoreException("Can't store " + objToBeStored + " at index: " + i + " in array of type: " + componentType + ": " + ase.getMessage()); } catch (Exception e) { e.printStackTrace(); } } if (array.length > size) { array[size] = null; } return array; } public boolean containsAll(Collection<?> c) { Iterator<?> iterator = c.iterator(); while(iterator.hasNext()){ if(!contains(iterator.next())){ return false; } } return true; } /** * @see java.util.Collection#addAll(java.util.Collection) */ public boolean addAll(Collection<? extends OTObject> c) { for (OTObject object : c) { add(object); } return true; } /** * Unsupported * @see java.util.Collection#removeAll(java.util.Collection) */ public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(); } /** * Unsupported * @see java.util.Collection#retainAll(java.util.Collection) */ public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); } }