Return-Path: Delivered-To: apmail-db-jdo-commits-archive@www.apache.org Received: (qmail 91556 invoked from network); 19 Mar 2005 05:31:47 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 19 Mar 2005 05:31:47 -0000 Received: (qmail 4747 invoked by uid 500); 19 Mar 2005 05:31:46 -0000 Mailing-List: contact jdo-commits-help@db.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: jdo-dev@db.apache.org Delivered-To: mailing list jdo-commits@db.apache.org Delivered-To: moderator for jdo-commits@db.apache.org Received: (qmail 90412 invoked by uid 99); 19 Mar 2005 01:12:53 -0000 X-ASF-Spam-Status: No, hits=-9.7 required=10.0 tests=ALL_TRUSTED,EXCUSE_3,NO_REAL_NAME X-Spam-Check-By: apache.org Message-ID: <20050319010609.28213.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Mailer: svnmailer-1.0.0-dev Date: Sat, 19 Mar 2005 01:06:09 -0000 Subject: svn commit: r158176 [44/79] - in incubator/jdo/trunk/ri11: ./ src/ src/conf/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/jdo/ src/java/org/apache/jdo/ejb/ src/java/org/apache/jdo/enhancer/ src/java/org/apache/jdo/impl/ src/java/org/apache/jdo/impl/enhancer/ src/java/org/apache/jdo/impl/enhancer/classfile/ src/java/org/apache/jdo/impl/enhancer/core/ src/java/org/apache/jdo/impl/enhancer/generator/ src/java/org/apache/jdo/impl/enhancer/meta/ src/java/org/apache/jdo/impl/enhancer/meta/model/ src/java/org/apache/jdo/impl/enhancer/meta/prop/ src/java/org/apache/jdo/impl/enhancer/meta/util/ src/java/org/apache/jdo/impl/enhancer/util/ src/java/org/apache/jdo/impl/fostore/ src/java/org/apache/jdo/impl/jdoql/ src/java/org/apache/jdo/impl/jdoql/jdoqlc/ src/java/org/apache/jdo/impl/jdoql/scope/ src/java/org/apache/jdo/impl/jdoql/tree/ src/java/org/apache/jdo/impl/model/ src/java/org/apache/jdo/impl/model/java/ src/java/org/apache/jdo/impl/model/java/runtime/ src/java/org/apache/jdo/impl/model/jdo/ src/java/org/apache/jdo/impl/model/jdo/caching/ src/java/org/apache/jdo/impl/model/jdo/util/ src/java/org/apache/jdo/impl/model/jdo/xml/ src/java/org/apache/jdo/impl/pm/ src/java/org/apache/jdo/impl/sco/ src/java/org/apache/jdo/impl/state/ src/java/org/apache/jdo/jdoql/ src/java/org/apache/jdo/jdoql/tree/ src/java/org/apache/jdo/model/ src/java/org/apache/jdo/model/java/ src/java/org/apache/jdo/model/jdo/ src/java/org/apache/jdo/pm/ src/java/org/apache/jdo/sco/ src/java/org/apache/jdo/state/ src/java/org/apache/jdo/store/ src/java/org/apache/jdo/util/ test/ test/conf/ test/enhancer/ test/enhancer/sempdept/ test/enhancer/sempdept/src/ test/enhancer/sempdept/src/empdept/ test/fsuid2/ test/fsuid2/org/ test/fsuid2/org/apache/ test/fsuid2/org/apache/jdo/ test/fsuid2/org/apache/jdo/pc/ test/java/ test/java/org/ test/java/org/apache/ test/java/org/apache/jdo/ test/java/org/apache/jdo/impl/ test/java/org/apache/jdo/impl/fostore/ test/java/org/apache/jdo/pc/ test/java/org/apache/jdo/pc/appid/ test/java/org/apache/jdo/pc/empdept/ test/java/org/apache/jdo/pc/serializable/ test/java/org/apache/jdo/pc/xempdept/ test/java/org/apache/jdo/test/ test/java/org/apache/jdo/test/query/ test/java/org/apache/jdo/test/util/ test/jdo/ test/jdo/org/ test/jdo/org/apache/ test/jdo/org/apache/jdo/ test/jdo/org/apache/jdo/pc/ test/jdo/org/apache/jdo/pc/appid/ test/jdo/org/apache/jdo/pc/empdept/ test/jdo/org/apache/jdo/pc/serializable/ test/jdo/org/apache/jdo/pc/xempdept/ xdocs/ To: jdo-commits@db.apache.org From: mbo@apache.org X-Virus-Checked: Checked X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/Date.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/ap= ache/jdo/impl/sco/Date.java?view=3Dauto&rev=3D158176 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/Date.java (ad= ded) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/Date.java Fri= Mar 18 17:02:29 2005 @@ -0,0 +1,247 @@ +/* + * Copyright 2005 The Apache Software Foundation. + *=20 + * 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=20 + *=20 + * http://www.apache.org/licenses/LICENSE-2.0 + *=20 + * Unless required by applicable law or agreed to in writing, software=20 + * distributed under the License is distributed on an "AS IS" BASIS,=20 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied= .=20 + * See the License for the specific language governing permissions and=20 + * limitations under the License. + */ + +package org.apache.jdo.impl.sco; + +import javax.jdo.JDOHelper; + +import org.apache.jdo.sco.SCO; +import org.apache.jdo.sco.SCODate; +import org.apache.jdo.state.StateManagerInternal; + + + +/** + * A mutable 2nd class object that represents java.util.Date. + * @author Marina Vatkina + * @version 1.0 + * @see java.util.Date + */ +public class Date extends java.util.Date implements SCODate { + + private transient StateManagerInternal owner; + + private transient int fieldNumber =3D -1; + + private final static String _Date =3D "Date"; // NOI18N + + /** + * Creates a Date object that represents the time at which + * it was allocated. + */ + public Date() { + super(); + } + + /** + * Creates a Date object that represents the given time + * in milliseconds. + * @param date the number of milliseconds + */ + public Date(long date) { + super(date); + } + + /** + * Sets the Date object to represent a point in time that is + * time milliseconds after January 1, 1970 00:00:00 GMT. + * =20 + * @param time the number of milliseconds. + * @see java.util.Date + */ =20 + public void setTime(long time) { + SCOHelper.debug(_Date, "setTime"); // NOI18N + + this.makeDirty(); + super.setTime(time); + } + + /** + * Creates and returns a copy of this object. + * + *

Mutable Second Class Objects are required to provide a public + * clone method in order to allow for copying PersistenceCapable + * objects. In contrast to Object.clone(), this method must not throw a + * CloneNotSupportedException. + */ + public Object clone() { + SCOHelper.debug(_Date, "clone"); // NOI18N + + Object obj =3D super.clone(); + if (obj instanceof SCO)=20 + ((SCO)obj).unsetOwner(owner, fieldNumber); + + return obj; + } + + /** -----------Depricated Methods------------------*/ + + /** + * Sets the year of this Date object to be the specified + * value plus 1900.=20 + * =20 + * @param year the year value. + * @see java.util.Calendar + * @see java.util.Date + * @deprecated As of JDK version 1.1, + * replaced by Calendar.set(Calendar.YEAR, year + 1900). + */ =20 + public void setYear(int year) { + SCOHelper.debug(_Date, "setYear"); // NOI18N + + this.makeDirty(); + super.setYear(year); + } =20 + + /** + * Sets the month of this date to the specified value. =20 + * @param month the month value between 0-11. + * @see java.util.Calendar + * @see java.util.Date + * @deprecated As of JDK version 1.1, + * replaced by Calendar.set(Calendar.MONTH, int month). + */ + public void setMonth(int month) { + SCOHelper.debug(_Date, "setMonth"); // NOI18N + + this.makeDirty(); + super.setMonth(month); + } =20 + + /** + * Sets the day of the month of this Date object to the + * specified value.=20 + * =20 + * @param date the day of the month value between 1-31. + * @see java.util.Calendar + * @see java.util.Date + * @deprecated As of JDK version 1.1, + * replaced by Calendar.set(Calendar.DAY_OF_MONTH, int date). + */ =20 + public void setDate(int date) { + SCOHelper.debug(_Date, "setDate"); // NOI18N + + this.makeDirty(); + super.setDate(date); + }=20 + + /** + * Sets the hour of this Date object to the specified value. + * =20 + * @param hours the hour value. + * @see java.util.Calendar + * @see java.util.Date + * @deprecated As of JDK version 1.1, + * replaced by Calendar.set(Calendar.HOUR_OF_DAY, int hours). + */ =20 + public void setHours(int hours) { + SCOHelper.debug(_Date, "setHours"); // NOI18N + + this.makeDirty(); + super.setHours(hours); + } =20 + + /** + * Sets the minutes of this Date object to the specified valu= e=2E + * =20 + * @param minutes the value of the minutes. + * @see java.util.Calendar + * @see java.util.Date + * @deprecated As of JDK version 1.1, + * replaced by Calendar.set(Calendar.MINUTE, int minutes). + */ + public void setMinutes(int minutes) { + SCOHelper.debug(_Date, "setMinutes"); // NOI18N + + this.makeDirty(); + super.setMinutes(minutes); + } =20 +=20 + /** + * Sets the seconds of this Date to the specified value. + * =20 + * @param seconds the seconds value. + * @see java.util.Calendar + * @see java.util.Date + * @deprecated As of JDK version 1.1, + * replaced by Calendar.set(Calendar.SECOND, int seconds). + */ =20 + public void setSeconds(int seconds) { + SCOHelper.debug(_Date, "setSeconds"); // NOI18N + + this.makeDirty(); + super.setSeconds(seconds); + }=20 + + /** ---------------- internal methods ------------------- */ + + /** + * Sets the Date object without notification of the Owner + * field. Used internaly to populate date from DB + * =20 + * @param time the number of milliseconds. + * @see java.util.Date + */ =20 + public void setTimeInternal(long time) { + super.setTime(time); + } + + /** + * @see SCO#unsetOwner(Object owner, int fieldNumber) + */ + public void unsetOwner(Object owner, int fieldNumber) {=20 + // Unset only if owner and fieldNumber match. + if (this.owner =3D=3D owner && this.fieldNumber =3D=3D fieldNumber= ) { + this.owner =3D null;=20 + this.fieldNumber =3D -1; + } + } + + /** + * @see SCO#setOwner (Object owner, int fieldNumber) + */ + public void setOwner (Object owner, int fieldNumber) { + // Set only if it was not set before. + if (this.owner =3D=3D null && owner instanceof StateManagerInterna= l) { + this.owner =3D (StateManagerInternal)owner; =20 + this.fieldNumber =3D fieldNumber; + } + } + + /**=20 + * @see SCO#getOwner () + */ =20 + public Object getOwner() { =20 + return SCOHelper.getOwner(owner); + }=20 +=20 + /** =20 + * @see SCO#getOwner () + */ =20 + public String getFieldName() { + return SCOHelper.getFieldName(owner, fieldNumber); =20 + } + + /** + * Marks object dirty + */ + private void makeDirty() { + if (owner !=3D null) { + owner.makeDirty(fieldNumber); // + }=20 + } =20 + =20 +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/Freezer.ja= va URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/ap= ache/jdo/impl/sco/Freezer.java?view=3Dauto&rev=3D158176 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/Freezer.java = (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/Freezer.java = Fri Mar 18 17:02:29 2005 @@ -0,0 +1,403 @@ +/* + * Copyright 2005 The Apache Software Foundation. + *=20 + * 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=20 + *=20 + * http://www.apache.org/licenses/LICENSE-2.0 + *=20 + * Unless required by applicable law or agreed to in writing, software=20 + * distributed under the License is distributed on an "AS IS" BASIS,=20 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied= .=20 + * See the License for the specific language governing permissions and=20 + * limitations under the License. + */ + +/* + * Freezer.java + * + */ + +package org.apache.jdo.impl.sco; + +import java.io.PrintStream; + +import java.lang.Comparable; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.HashMap; +import java.util.TreeMap; +import java.util.TreeSet; + +import javax.jdo.JDOFatalInternalException; +import javax.jdo.JDOUserException; +import javax.jdo.JDOHelper; + +import javax.jdo.spi.PersistenceCapable; + +import org.apache.jdo.sco.SCO; +import org.apache.jdo.sco.SCOCollection; +import org.apache.jdo.sco.SCOMap; +import org.apache.jdo.state.StateManagerInternal; +import org.apache.jdo.util.I18NHelper; + + +/** Freezer is a helper class with static methods to assist + * transcribing non-ordered collection and map classes. + * When an unordered collection or map is written to the datastore + * and subsequently fetched, the order of elements can change. + * This causes optimistic failures for persistent instances that + * contain unordered collections even if the collection has not + * changed. + * Another issue solved by the Freezer is that if the user defines + * an ordering or hashing based on persistent values of the + * elements, during transcribing while fetching the instance from + * the datastore the persistent instance must be fetched. This + * causes recursion in the fetch process. + * Freezing is the process of iterating the elements of a collection + * or iterating the entrySet elements of a map and constructing an + * array of elements in an absolute ordering. Freezing is done + * during transcribing for storage. + * Thawing is the process of iterating the frozen elements and + * storing them in their user-visible order or using their + * user-visible hashCode. + * A collection or map is frozen when read from the datastore, + * and thawed upon first application use. + * @author Craig Russell + * @version 1.0.2 + * @since 1.0.1 + */ +public class Freezer { + =20 + /** This class currently is not used as a class but only as a helper. + * This constructor is for future use if needed. + */ + protected Freezer() { + } + =20 + /**=20 + * Holds a mapping of java.util and java.sql type names to the names=20 + * of their corresponding SCO types.=20 + */ + static Map convertedClassNames; + + /** Initialize convertedClassNames map. */ + static { + convertedClassNames =3D new HashMap(12); + convertedClassNames.put("java.util.ArrayList", "org.apache.jdo.imp= l=2Esco.ArrayList"); // NOI18N + convertedClassNames.put("java.util.Date", "org.apache.jdo.impl.sco= .Date"); // NOI18N + convertedClassNames.put("java.util.HashMap", "org.apache.jdo.impl.= sco.HashMap"); // NOI18N + convertedClassNames.put("java.util.HashSet", "org.apache.jdo.impl.= sco.HashSet"); // NOI18N + convertedClassNames.put("java.util.Hashtable", "org.apache.jdo.imp= l=2Esco.Hashtable"); // NOI18N + convertedClassNames.put("java.util.LinkedList", "org.apache.jdo.im= pl.sco.LinkedList"); // NOI18N + convertedClassNames.put("java.sql.Date", "org.apache.jdo.impl.sco.= SqlDate"); // NOI18N + convertedClassNames.put("java.sql.Time", "org.apache.jdo.impl.sco.= SqlTime"); // NOI18N + convertedClassNames.put("java.sql.Timestamp", "org.apache.jdo.impl= .sco.SqlTimestamp"); // NOI18N + convertedClassNames.put("java.util.TreeMap", "org.apache.jdo.impl.= sco.TreeMap"); // NOI18N + convertedClassNames.put("java.util.TreeSet", "org.apache.jdo.impl.= sco.TreeSet"); // NOI18N + convertedClassNames.put("java.util.Vector", "org.apache.jdo.impl.s= co.Vector"); // NOI18N + } + + /** + * I18N message handler + */ =20 + private final static I18NHelper msg =3D I18NHelper.getInstance( + "org.apache.jdo.impl.sco.Bundle"); // NOI18N + + /** Provide a frozen array of elements from a Set. This method + * does not actually freeze the Set but simply calculates the + * frozen elements. + * @return the Object[] containing the frozen elements. + * @param size the number of elements in the collection. + * @param set the Set whose elements are to be ordered. + */ =20 + static public Object[] freeze(Collection set, int size) { + Object[] result =3D new Object[size]; + Iterator it; + if (set instanceof SCOCollection) { + it =3D ((SCOCollection)set).eitherIterator(); + } else { + it =3D set.iterator(); + } + TreeSet ts =3D new TreeSet(new AbsoluteOrdering()); + while (it.hasNext()) { + ts.add(it.next()); + } + return ts.toArray(result); + } + =20 + /** Provide a frozen array of elements from the entrySet of a Map. + * This method + * does not actually freeze the Map but simply calculates the + * frozen elements. + * @return the Map.Entry[] of elements in the map. + * @param size the number of entries in the map. + * @param map the Map whose entrySet elements are to be calculated. + */ =20 + static public Map.Entry[] freeze(Map map, int size) { + Map.Entry[] result =3D new Map.Entry[size]; + if (size !=3D 0) { + TreeMap tm =3D new TreeMap(new AbsoluteOrdering()); + Iterator it; + if (map instanceof SCOMap) { + it =3D ((SCOMap)map).eitherIterator(); + } else { + it =3D map.entrySet().iterator(); + } + while (it.hasNext()) { + Map.Entry me =3D (Map.Entry)it.next(); + tm.put(me.getKey(), me.getValue()); + } + return (Map.Entry[])tm.entrySet().toArray(result); + } else { + return result; + } + } + =20 + /** Provide a frozen iterator of elements from the entrySet of a Map. + * This method + * does not actually freeze the Map but simply calculates the + * frozen elements and returns an iterator over them. + * @return an iterator over Map.Entry[] of elements in the map. + * @param size the number of entries in the map. + * @param map the Map whose entrySet elements are to be calculated. + */ =20 + static public Iterator frozenIterator(Map map, int size) { + TreeMap tm =3D new TreeMap(new AbsoluteOrdering()); + if (size !=3D 0) { + Iterator it; + if (map instanceof SCOMap) { + it =3D ((SCOMap)map).eitherIterator(); + } else { + it =3D map.entrySet().iterator(); + } + while (it.hasNext()) { + Map.Entry me =3D (Map.Entry)it.next(); + tm.put(me.getKey(), me.getValue()); + } + } + return tm.entrySet().iterator(); + } + =20 + /** Create a map whose elements are ordered according to an + * absolute ordering. + * @return the map with an absolute ordering Comparator. + */ =20 + static public Map createAbsoluteOrderMap() { + return new TreeMap(new AbsoluteOrdering()); + } + =20 + /** Create an iterator over frozen elements or entries. + * @param frozen the array of frozen entries or elements. + * @return the iterator over the entries or elements. + */ + static public Iterator createFrozenIterator(Object[] frozen) { + return new FrozenIterator(frozen); + } + =20 + /** Thaw the frozen elements of a map. If the elements are frozen, + * retrieve them from the datastore and internally add them. Then retu= rn + * the frozen state since they're not frozen any more. + * @param map the Map to be thawed. + * @param owner the StateManager that owns this Map. + * @param frozenEntries the frozen entries to be thawed. + * @return the new contents of frozenEntries. + */ + static public Map.Entry[] thaw(SCOMap map, StateManagerInternal owner, + Map.Entry[] frozenEntries) { + if (frozenEntries =3D=3D null || frozenEntries.length =3D=3D 0) { + return null; + } + int length =3D frozenEntries.length; + if (owner !=3D null) { + // only fetch PC elements from the store + ArrayList persistenceCapables =3D new ArrayList(); + for (int i =3D 0; i < length; ++i) { + Map.Entry mapEntry =3D frozenEntries[i]; + Object key =3D mapEntry.getKey(); + Object value =3D mapEntry.getValue(); + if (key instanceof PersistenceCapable) { + persistenceCapables.add(key); + } + if (value instanceof PersistenceCapable) { + persistenceCapables.add(value); + } + } + try { + owner.getPersistenceManager().retrieveAll(persistenceCapab= les); + } catch (JDOUserException ex) { + // if objects don't exist, this exception is expected. + // this will be handled later when the user iterates the=20 + // collection if values are needed. + } + } + for (int i =3D 0; i < length; ++i) { + Map.Entry mapEntry =3D (Map.Entry)frozenEntries[i]; + Object key =3D mapEntry.getKey(); + Object value =3D mapEntry.getValue(); + map.putInternal(key, value); + } + // reset the caller's frozenEntries. + return null; + } + =20 + /** Thaw the frozen elements of a collection. If the elements are froz= en, + * retrieve them from the datastore and internally add them. Then reset + * the frozen state since they're not frozen any more. + * @param sco the frozen collection to be thawed. + * @param owner the StateManager that owns this collection. + * @param frozenElements the elements to be thawed. + * @return the new contents of frozenElements. + */ + static public Object[] thaw(SCOCollection sco, StateManagerInternal ow= ner, + Object[] frozenElements) { + if (frozenElements =3D=3D null || frozenElements.length =3D=3D 0) { + return null; + } + int length =3D frozenElements.length; + if (owner !=3D null) { + // only fetch PC elements from the store + ArrayList persistenceCapables =3D new ArrayList(); + for (int i =3D 0; i < length; ++i) { + Object element =3D frozenElements[i]; + if (element instanceof PersistenceCapable) { + persistenceCapables.add(element); + } + } + try { + owner.getPersistenceManager().retrieveAll(persistenceCapab= les); + } catch (JDOUserException ex) { + // if objects don't exist, this exception is expected. + // this will be handled later when the user iterates the=20 + // collection if values are needed. + } + } + for (int i =3D 0; i < length; ++i) { + sco.addInternal(frozenElements[i]); + } + // reset the caller's frozenElements. + return null; + } + =20 + /** This class is the Comparator to impose an absolute ordering + * on instances of PersistenceCapable, wrapper, and mutable sco types. + */ =20 + static class AbsoluteOrdering implements Comparator { + /** Implement an absolute ordering comparison for persistence + * capable, wrapper, and mutable sco types. + * @return -1, 0, or 1 if the first parameter is less that, equal = to, or greater than the second parameter. + * @param o1 the first parameter. + * @param o2 the second parameter. */ =20 + public int compare(Object o1, Object o2) { + String className1 =3D convertClassName(o1.getClass().getName()= ); + String className2 =3D convertClassName(o2.getClass().getName()= ); + if (className1!=3DclassName2) { + // order by class names + return className1.compareTo(className2); + } else { + // class names are the same (modulo sco Date types) + if (o1 instanceof PersistenceCapable) { + // compare oids + Object oid1 =3D JDOHelper.getObjectId(o1); + Object oid2 =3D JDOHelper.getObjectId(o2); + if (oid1 instanceof Comparable) { + if (oid1 !=3D null) { + return ((Comparable)oid1).compareTo(oid2); + } else if (o1 instanceof Comparable) { + return ((Comparable)o1).compareTo(o2); + } else { + return o1.hashCode() - o2.hashCode(); + } + } else { + throw new JDOFatalInternalException(msg.msg("EXC_O= idNotComparable", // NOI18N + oid1.getClass().getName())); + } + } else if (o1 instanceof Comparable) { + // immutables (String, Integer, etc.) and Date use thi= s code path + return ((Comparable)o1).compareTo(o2); + } else { + // give up! compare instance.toString(). + // this is used by java.util.Locale that doesn't imple= ment Comparable. + return (o1.toString().compareTo(o2.toString())); + } + } + } + } + =20 + /** Convert the class name to make non sco types compare to + * their corresponding sco types. + * @param className the actual class name. + * @return the corresponding sco class name. + */ =20 + static String convertClassName(String className) { + String converted =3D (String)convertedClassNames.get(className); + if (converted =3D=3D null) + converted =3D className; + return converted; + } + =20 + /** This class iterates over an array. + */ =20 + protected static class FrozenIterator implements Iterator { + /** The array over which to iterate. + */ =20 + Object[] frozen; + int idx =3D 0; + int length =3D 0; + FrozenIterator(Object[] frozen) { + this.frozen =3D frozen; + length =3D frozen.length; + } + /** Return the next entry of the iteration. + * @return the next entry of the iteration. + */ =20 + public Object next() {return frozen[idx++];} + /** Return true if the iteration is not complete. + * @return true if the iteration is not complete. + */ =20 + public boolean hasNext() {return idx < length;} + /** This operation is not supported. + */ =20 + public void remove() {throw new JDOFatalInternalException( + msg.msg("EXC_RemoveNotSupported"));} // NOI18N + } + + /** For debugging, print the contents of a frozen entrySet. + * @param p where to write the output. + * @param s an identifying string. + * @param entries the Map.Entry[] to print. + */ =20 + static public void printEntries(PrintStream p, String s, Map.Entry[] e= ntries) { + p.println(s); + for (int i =3D 0; i < entries.length; ++i) { + Map.Entry entry =3D entries[i]; + Object key =3D entry.getKey(); + Object value =3D entry.getValue(); + Object keyoid =3D JDOHelper.getObjectId(key); + Object valueoid =3D JDOHelper.getObjectId(value); + p.println("Key: " + key.toString() + " OID: " + keyoid.toStrin= g()); // NOI18N + p.println("Value: " + value.toString() + " OID: " + valueoid.t= oString()); // NOI18N + } + } + =20 + /** For debugging, print the contents of a frozen collection. + * @param p where to write the output. + * @param s an identifying string. + * @param elements the Object[] to print. + */ =20 + static public void printElements(PrintStream p, String s, Object[] ele= ments) { + p.println(s); + for (int i =3D 0; i < elements.length; ++i) { + Object element =3D elements[i]; + Object oid =3D JDOHelper.getObjectId(element); + p.println("Element: " + oid); // NOI18N + } + } + =20 +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/HashMap.ja= va URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/ap= ache/jdo/impl/sco/HashMap.java?view=3Dauto&rev=3D158176 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/HashMap.java = (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/HashMap.java = Fri Mar 18 17:02:29 2005 @@ -0,0 +1,607 @@ +/* + * Copyright 2005 The Apache Software Foundation. + *=20 + * 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=20 + *=20 + * http://www.apache.org/licenses/LICENSE-2.0 + *=20 + * Unless required by applicable law or agreed to in writing, software=20 + * distributed under the License is distributed on an "AS IS" BASIS,=20 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied= .=20 + * See the License for the specific language governing permissions and=20 + * limitations under the License. + */ + +/*=20 + * sco.HashMap.java + */ +=20 +package org.apache.jdo.impl.sco; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.jdo.JDOFatalInternalException; +import javax.jdo.JDOHelper; +import javax.jdo.JDOUserException; + +import org.apache.jdo.sco.SCO; +import org.apache.jdo.sco.SCOMap; +import org.apache.jdo.state.StateManagerInternal; +import org.apache.jdo.util.I18NHelper; + + +/** + * A mutable 2nd class object that represents HashMap. + * @author Marina Vatkina + * @version 1.0.1 + * @see java.util.HashMap + */ +public class HashMap extends java.util.HashMap implements SCOMap { + + private transient StateManagerInternal owner; + + private transient int fieldNumber =3D -1; + + private transient Class keyType; + + private transient Class valueType; + + private transient boolean allowNulls; + + private transient java.util.ArrayList addedKeys =3D new java.util.Arra= yList(); + private transient java.util.ArrayList addedValues =3D new java.util.Ar= rayList(); + + private transient java.util.ArrayList removedKeys =3D new java.util.Ar= rayList(); + private transient java.util.ArrayList removedValues =3D new java.util.= ArrayList(); + + private transient Map.Entry[] frozenEntries =3D null; + =20 + /**=20 + * I18N message handler=20 + */=20 + private final static I18NHelper msg =3D I18NHelper.getInstance(=20 + "org.apache.jdo.impl.sco.Bundle"); // NOI18N + + private final static String _HashMap =3D "HashMap"; // NOI18N + + /** =20 + * Creates a new empty HashMap object. + * =20 + * @param keyType the type of the keys allowed. + * @param valueType the type of the values allowed. + * @param allowNulls true if nulls are allowed. + * @see java.util.HashMap + */ =20 + public HashMap(Class keyType, Class valueType, boolean allowNulls) { + super();=20 + this.keyType =3D keyType; + this.valueType =3D valueType; + this.allowNulls =3D allowNulls; + }=20 + + /** =20 + * Creates a new empty HashMap object that has=20 + * the specified initial capacity. + * =20 + * @param keyType the type of the keys allowed. + * @param valueType the type of the values allowed. + * @param allowNulls true if nulls are allowed + * @param initialCapacity the initial capacity of the hash map.=20 + * @throws IllegalArgumentException if the initial capacity is less + * than zero. + * @see java.util.HashMap + */ =20 + public HashMap(Class keyType, Class valueType, boolean allowNulls,=20 + int initialCapacity) { + + super(initialCapacity);=20 + this.keyType =3D keyType; + this.valueType =3D valueType; + this.allowNulls =3D allowNulls; + }=20 + + /** =20 + * Creates a new empty HashMap object that has=20 + * the specified initial capacity.. + * =20 + * @param keyType the type of the keys allowed. + * @param valueType the type of the values allowed. + * @param allowNulls true if nulls are allowed + * @param initialCapacity the initial capacity of the hash map.=20 + * @param loadFactor the load factor of the hash map. + * @throws IllegalArgumentException if the initial capacity is less + * than zero. + * @see java.util.HashMap + */ =20 + public HashMap(Class keyType, Class valueType, boolean allowNulls,=20 + int initialCapacity, float loadFactor) { + + super(initialCapacity, loadFactor);=20 + this.keyType =3D keyType; + this.valueType =3D valueType; + this.allowNulls =3D allowNulls; + }=20 + + // -------------------------Public Methods------------------ + + /** + * Associates the specified value with the specified key in this map. + * If the map previously contained a mapping for this key, the old + * value is replaced. + * + * @param key key with which the specified value is to be associated. + * @param value value to be associated with the specified key. + * @return previous value associated with specified key, or null + * if there was no mapping for key. A null return can + * also indicate that the HashMap previously associated + * null with the specified key. + * @see java.util.HashMap + */ + public Object put(Object key, Object value) { + SCOHelper.debug(_HashMap, "put"); // NOI18N + + // Check both the key and the value: + Throwable[] err =3D new Throwable[2]; + int l =3D 0; + + try { + SCOHelper.assertNullKeysAllowed(key, allowNulls); + SCOHelper.assertKeyType(key, keyType); + } catch (Throwable ex) { + err[l++] =3D ex; + } + try { + SCOHelper.assertNullValuesAllowed(value, allowNulls); + SCOHelper.assertValueType(value, valueType); + } catch (Throwable ex) { + err[l++] =3D ex; + } + SCOHelper.validateResult(l, err); + + // Mark the field as dirty + this.makeDirty(); + + thaw(); + Object o =3D process(key, value); + + // Apply updates + this.trackUpdates(true); + + return o; + } =20 + + /** + * Copies all of the mappings from the specified map to this one. + * + * These mappings replace any mappings that this map had for any of the + * keys currently in the specified Map. + * + * @param t Mappings to be stored in this map. + * @see java.util.HashMap + */ =20 + public void putAll(Map t) { + SCOHelper.debug(_HashMap, "putAll"); // NOI18N + + // iterate the collection and make a list of wrong elements. + Throwable[] err =3D new Throwable[2*t.size()]; + int l =3D 0; + + Iterator i =3D t.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry e =3D (Map.Entry) i.next(); + Object k =3D e.getKey(); + Object v =3D e.getValue(); + + // Check both the key and the value: + try { + SCOHelper.assertNullKeysAllowed(k, allowNulls); + SCOHelper.assertKeyType(k, keyType); + } catch (Throwable ex) { + err[l++] =3D ex; + } + try { + SCOHelper.assertNullValuesAllowed(v, allowNulls); + SCOHelper.assertValueType(v, valueType); + } catch (Throwable ex) { + err[l++] =3D ex; + } + } + SCOHelper.validateResult(l, err); + + boolean modified =3D false; + // Mark the field as dirty + this.makeDirty(); + + thaw(); + for (i =3D t.entrySet().iterator(); i.hasNext();) { + Map.Entry e =3D (Map.Entry) i.next();=20 + process(e.getKey(), e.getValue()); + } + + // Apply updates + this.trackUpdates(true); + }=20 + + /** + * Removes the mapping for this key from this map if present. + * + * @param key key whose mapping is to be removed from the map. + * @return previous value associated with specified key, or null + * if there was no mapping for key. A null return can + * also indicate that the map previously associated null + * with the specified key. + * @see java.util.HashMap + */ =20 + public Object remove(Object key) { + SCOHelper.debug(_HashMap, "remove"); // NOI18N + + // Mark the field as dirty + this.makeDirty(); + + thaw(); + boolean removed =3D false; + + // Nothing is added to the removed collections if the key has not + // been in the map before: + if (super.containsKey(key)) { + if (addedKeys.remove(key) =3D=3D false) { + removedKeys.add(key); + } + removed =3D true; + } + + Object o =3D super.remove(key); + + // Remove old value if there was one: + if (removed && addedValues.remove(o) =3D=3D false) { + removedValues.add(o); + } + + // Apply updates + this.trackUpdates(removed); + + return o; + } =20 + + + /** + * Removes all of the elements from this map. + * @see java.util.HashMap + */ =20 + public void clear() { + SCOHelper.debug(_HashMap, "clear"); // NOI18N + + // Mark the field as dirty + this.makeDirty(); + + thaw(); + for (Iterator i =3D super.entrySet().iterator(); i.hasNext();) { + Map.Entry e =3D (Map.Entry) i.next(); + removedKeys.add(e.getKey()); + removedValues.add(e.getValue()); + } + + super.clear(); + addedKeys.clear(); + addedValues.clear(); + + // Apply updates + this.trackUpdates(true); + + } =20 +=20 + /** + * Creates and returns a copy of this object. + * =20 + *

Mutable Second Class Objects are required to provide a public + * clone method in order to allow for copying PersistenceCapable + * objects. In contrast to Object.clone(), this method must not throw a + * CloneNotSupportedException. + */ =20 + public Object clone() { + SCOHelper.debug(_HashMap, "clone"); // NOI18N + + Object obj =3D super.clone(); + if (obj instanceof SCO) { + ((SCO)obj).unsetOwner(owner, fieldNumber); + } + return obj; + } + + /** These methods need to thaw the map before performing the operation. + */ + public boolean containsKey(Object key) { + thaw(); + return super.containsKey(key); + } + public boolean containsValue(Object value) { + thaw(); + return super.containsValue(value); + } + public Set entrySet() { + thaw(); + return super.entrySet(); + } + public boolean equals(Object o) { + thaw(); + return super.equals(o); + } + public Object get(Object key) { + thaw(); + return super.get(key); + } + public int hashCode() { + thaw(); + return super.hashCode(); + } + public boolean isEmpty() { + thaw(); + return super.isEmpty(); + } + public Set keySet() { + thaw(); + return super.keySet(); + } + public int size() { + if (isFrozen()) { + return frozenEntries.length; + } else { + return super.size(); + } + } + public Collection values() { + thaw(); + return super.values(); + } + public String toString() { + thaw(); + return super.toString(); + } + =20 + /** + * @see SCOMap#reset() + */ =20 + public void reset() { + addedKeys.clear(); + addedValues.clear(); + removedKeys.clear(); + removedValues.clear(); + } + + /** + * @see SCOMap#putInternal(Object key, Object value) + */ =20 + public void putInternal(Object key, Object value) { + super.put(key, value); + } + + + /** + * @see SCOMap#putAllInternal(Map t) + */ =20 + public void putAllInternal(Map t) { + for (Iterator i =3D t.entrySet().iterator(); i.hasNext();) { + Map.Entry e =3D (Map.Entry) i.next(); + super.put(e.getKey(), e.getValue()); + } + } =20 + + /**=20 + * @see SCOMap#getAddedKeys() + */ =20 + public Collection getAddedKeys() {=20 + return (Collection)addedKeys;=20 + } =20 +=20 + /**=20 + * @see SCOMap#getAddedValues() + */ =20 + public Collection getAddedValues() {=20 + return (Collection)addedValues;=20 + } =20 +=20 + /** =20 + * @see SCOMap#getRemovedKeys() + */ =20 + public Collection getRemovedKeys() { =20 + return (Collection)removedKeys; =20 + } =20 + + /** =20 + * @see SCOMap#getRemovedValues() + */ =20 + public Collection getRemovedValues() { =20 + return (Collection)removedValues; =20 + } =20 + + /**=20 + * @see SCOMap#clearInternal() + */ =20 + public void clearInternal() { =20 + super.clear();=20 + this.reset();=20 + } =20 + + /** + * @see SCOMap#removeInternal(Object key) + */ =20 + public void removeInternal(Object key) { + super.remove(key); + } + + /** + * @see SCO#unsetOwner(Object owner, int fieldNumber) + */ + public void unsetOwner(Object owner, int fieldNumber) {=20 + // Unset only if owner and fieldNumber match. + if (this.owner =3D=3D owner && this.fieldNumber =3D=3D fieldNumber= ) { + this.owner =3D null;=20 + this.fieldNumber =3D -1; + } + } + + /** + * @see SCO#setOwner (Object owner, int fieldNumber) + */ + public void setOwner (Object owner, int fieldNumber) { + // Set only if it was not set before. + if (this.owner =3D=3D null && owner instanceof StateManagerInterna= l) { + this.owner =3D (StateManagerInternal)owner; =20 + this.fieldNumber =3D fieldNumber; + } + } + + /**=20 + * @see SCO#getOwner() + */ =20 + public Object getOwner() { =20 + return SCOHelper.getOwner(owner); + }=20 +=20 + /** =20 + * @see SCO#getFieldName() + */ =20 + public String getFieldName() { + return SCOHelper.getFieldName(owner, fieldNumber); =20 + } + + /** + * Notify StateManager to mark field as dirty. + */ + private void makeDirty() { + if (owner !=3D null) { + owner.makeDirty(fieldNumber); // + } + } =20 + /** + * Notify StateManager to process the changes. + */ =20 + private void trackUpdates(boolean modified) { + if (modified && owner !=3D null) { + owner.trackUpdates(fieldNumber, this); + } + } + + /** + * @see SCOMap#getKeyType() { + */ =20 + public Class getKeyType() { + return keyType; + } + + /** + * @see SCOMap#getValueType() { + */ =20 + public Class getValueType() { + return valueType; + } + + /** + * @see SCOMap#allowNulls() { + */ =20 + public boolean allowNulls() { + return allowNulls; + } + + /** + * Processes single put operation in this map. + * @param key key with which the specified value is to be associated. + * @param value value to be associated with the specified key. + * @return previous value associated with specified key, or null + * if there was no mapping for key.=20 + */ + private Object process(Object key, Object value) { + thaw(); + // Key is added to the addedKeys collection only if it has not + // been in the map before: + if (!super.containsKey(key)) { + if (removedKeys.remove(key) =3D=3D false) { + addedKeys.add(key); + } + } + + Object o =3D super.put(key, value); + + // Remove old value: + if (addedValues.remove(o) =3D=3D false) { + removedValues.add(o); + } + + // Add new value: + if (removedValues.remove(value) =3D=3D false) { + addedValues.add(value); + } + + return o; + } + =20 + /** Returns the frozen state of this Map. + * @since 1.0.1 + * @return the frozen state. + */ =20 + private boolean isFrozen() { + return frozenEntries !=3D null; + } + =20 + /** Returns the frozen contents of this Map as a Map.Entry[]. + * @return the frozen entries. + * @since 1.0.1 + */ + private Map.Entry[] getFrozen() { + if (!isFrozen()) { + frozenEntries =3D Freezer.freeze(this, super.size()); + } + return frozenEntries; + } + =20 + /** Set the contents of this Map from the frozen entries. + * @since 1.0.1 + * @param entries the frozen entries + */ + public void setFrozen(Map.Entry[] entries) { + frozenEntries =3D entries; + } + =20 + /** Get an iterator regardless of whether the map is frozen. + * If frozen, get a frozen iterator. + * If thawed, get a regular iterator. + * @since 1.0.1 + * @return the iterator. + */ + public Iterator eitherIterator() { + if (isFrozen()) { + return frozenIterator(); + } else { + return super.entrySet().iterator(); + } + } + =20 + /** Get an iterator over the frozen elements of this map. This allows + * iteration of the elements without thawing them, as is needed for + * transcription. + * @since 1.0.1 + * @return the iterator. + */ + public Iterator frozenIterator() { + return Freezer.createFrozenIterator(getFrozen()); + } + =20 + /** + * Thaw the frozen elements of this map. If the elements are frozen, + * retrieve them from the datastore and internally add them. Then reset + * the frozen state since they're not frozen any more. + * @since 1.0.1 + */ + private void thaw() { + if (isFrozen()) { + frozenEntries =3D Freezer.thaw(this, owner, frozenEntries); + } + } + =20 +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/HashSet.ja= va URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/ap= ache/jdo/impl/sco/HashSet.java?view=3Dauto&rev=3D158176 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/HashSet.java = (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/HashSet.java = Fri Mar 18 17:02:29 2005 @@ -0,0 +1,589 @@ +/* + * Copyright 2005 The Apache Software Foundation. + *=20 + * 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=20 + *=20 + * http://www.apache.org/licenses/LICENSE-2.0 + *=20 + * Unless required by applicable law or agreed to in writing, software=20 + * distributed under the License is distributed on an "AS IS" BASIS,=20 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied= .=20 + * See the License for the specific language governing permissions and=20 + * limitations under the License. + */ + +/*=20 + * sco.HashSet.java + */ +=20 +package org.apache.jdo.impl.sco; + +import java.util.Collection; +import java.util.Iterator; + +import org.apache.jdo.sco.SCO; +import org.apache.jdo.sco.SCOCollection; +import org.apache.jdo.state.StateManagerInternal; +import org.apache.jdo.util.I18NHelper; + + + +/** + * A mutable 2nd class object that represents HashSet. + * @author Marina Vatkina + * @version 1.0.1 + * @see java.util.HashSet + */ +public class HashSet extends java.util.HashSet implements SCOCollection { + + private transient StateManagerInternal owner; + + private transient int fieldNumber =3D -1; + + private transient Class elementType; + + private transient boolean allowNulls; + + private transient java.util.HashSet added =3D new java.util.HashSet(); + + private transient java.util.HashSet removed =3D new java.util.HashSet(= ); + + private transient Object[] frozenElements =3D null; + =20 + /**=20 + * I18N message handler=20 + */=20 + private final static I18NHelper msg =3D I18NHelper.getInstance(=20 + "org.apache.jdo.impl.sco.Bundle"); // NOI18N + + private final static String _HashSet =3D "HashSet"; // NOI18N + + /** =20 + * Creates a new empty HashSet object. + * =20 + * @param elementType the element types allowed + * @param allowNulls true if nulls are allowed + * @see java.util.HashSet + */ =20 + public HashSet(Class elementType, boolean allowNulls) { + super();=20 + this.elementType =3D elementType; + this.allowNulls =3D allowNulls; + }=20 + + /** =20 + * Creates a new empty HashSet object that has=20 + * the specified initial capacity. + * =20 + * @param elementType the element types allowed + * @param allowNulls true if nulls are allowed + * @param initialCapacity the initial capacity of the hash map.=20 + * @throws IllegalArgumentException if the initial capacity is less + * than zero. + * @see java.util.HashSet + */ =20 + public HashSet(Class elementType, boolean allowNulls, int initialCapac= ity) { + super(initialCapacity);=20 + this.elementType =3D elementType; + this.allowNulls =3D allowNulls; + }=20 + + /** =20 + * Creates a new empty HashSet object that has=20 + * the specified initial capacity.. + * =20 + * @param elementType the element types allowed + * @param allowNulls true if nulls are allowed + * @param initialCapacity the initial capacity of the hash map.=20 + * @param loadFactor the load factor of the hash map. + * @throws IllegalArgumentException if the initial capacity is less + * than zero. + * @see java.util.HashSet + */ =20 + public HashSet(Class elementType, boolean allowNulls, int initialCapac= ity,=20 + float loadFactor) { + super(initialCapacity, loadFactor);=20 + this.elementType =3D elementType; + this.allowNulls =3D allowNulls; + }=20 + + // -------------------------Public Methods------------------ + + /** + * Adds the specified element to this set if it is not already + * present. + * =20 + * @param o element to be added to this set. + * @return true if the set did not already contain the specif= ied + * element. + * @see java.util.HashSet + */ + public boolean add(Object o) { + SCOHelper.debug(_HashSet, "add"); // NOI18N + + SCOHelper.assertNullsAllowed(o, allowNulls); + SCOHelper.assertElementType(o, elementType); + + // Mark the field as dirty + this.makeDirty(); + + thaw(); + =20 + boolean modified =3D super.add(o);=20 + if (modified) { + if (removed.remove(o) =3D=3D false) { + added.add(o); + } + } + + // Apply updates + this.trackUpdates(modified); + + return modified; + } =20 + + /** + * Adds all of the elements in the specified collection to this collec= tion + * =20 + * @param c collection whose elements are to be added to this collecti= on. + * @return true if this collection changed as a result of the + * call. + * @throws UnsupportedOperationException if the addAll method= is + * not supported by this collection. + * =20 + * @see java.util.AbstractCollection + * @see java.util.HashSet + */ =20 + public boolean addAll(Collection c) { + SCOHelper.debug(_HashSet, "addAll"); // NOI18N + + // iterate the collection and make a list of wrong elements. + Throwable[] err =3D new Throwable[c.size()]; + int l =3D 0; + + Iterator i =3D c.iterator(); + while (i.hasNext()) { + Object o =3D i.next(); + try { + SCOHelper.assertNullsAllowed(o, allowNulls); + SCOHelper.assertElementType(o, elementType); + } catch (Throwable e) { + err[l++] =3D e; + } + } + SCOHelper.validateResult(l, err); + + boolean modified =3D false; + // Mark the field as dirty + this.makeDirty(); + + thaw(); + =20 + for (Iterator iter =3D c.iterator(); iter.hasNext();) { + Object o =3D iter.next(); + if (!super.contains(o)) { + if (removed.remove(o) =3D=3D false) { + added.add(o); + } + super.add(o); + modified =3D true; + } + } + + // Apply updates + this.trackUpdates(modified); + + return modified; + }=20 + + /** + * Removes the given element from this set if it is present. + * =20 + * @param o object to be removed from this set, if present. + * @return true if the set contained the specified element. + * @see java.util.HashSet + */ =20 + public boolean remove(Object o) { + SCOHelper.debug(_HashSet, "remove"); // NOI18N + + // Mark the field as dirty + this.makeDirty(); + + thaw(); + =20 + boolean modified =3D super.remove(o); + if (modified) {=20 + if (added.remove(o) =3D=3D false) {=20 + removed.add(o); + }=20 + }=20 + + // Apply updates + this.trackUpdates(modified); + + return modified; + } =20 + + + /** + * Removes from this collection all of its elements that are contained= in + * the specified collection (optional operation).

+ * Processes each element remove internally not to have call backs + * into #remove(Object).=20 + * =20 + * @param c elements to be removed from this collection. + * @return true if this collection changed as a result of the + * call. + * =20 + * @throws UnsupportedOperationException removeAll is not supported + * by this collection. + * =20 + * @see java.util.HashSet + * @see java.util.AbstractCollection + */ =20 + public boolean removeAll(Collection c) { + SCOHelper.debug(_HashSet, "removeAll"); // NOI18N + + boolean modified =3D false; + // Mark the field as dirty + this.makeDirty(); + + thaw(); + =20 + for (Iterator iter =3D c.iterator(); iter.hasNext();) { + Object o =3D iter.next(); + if (super.contains(o)) { + removeInternal(o); + modified =3D true; + if (added.remove(o) =3D=3D false) { + removed.add(o); + } + } + } + + // Apply updates + this.trackUpdates(modified); + + return modified; + } + + /** + * Retains only the elements in this collection that are contained in = the + * specified collection (optional operation).=20 + * + * @return true if this collection changed as a result of the + * call. + * + * @throws UnsupportedOperationException if the retainAll met= hod + * is not supported by this collection. + * + * @see java.util.HashSet + * @see java.util.AbstractCollection + */ =20 + public boolean retainAll(Collection c) { + SCOHelper.debug(_HashSet, "retainAll"); // NOI18N + + // Mark the field as dirty + this.makeDirty(); + =20 + thaw(); + + for (Iterator iter =3D super.iterator(); iter.hasNext();) { + Object o =3D iter.next(); + if (!c.contains(o)) {=20 + if (added.remove(o) =3D=3D false) {=20 + removed.add(o);=20 + }=20 + }=20 + } + + boolean modified =3D super.retainAll(c); + + // Apply updates + this.trackUpdates(modified); + + return modified; + } + + /** + * Removes all of the elements from this set. + * @see java.util.HashSet + */ =20 + public void clear() { + SCOHelper.debug(_HashSet, "clear"); // NOI18N + + // Mark the field as dirty + this.makeDirty(); + + thaw(); + removed.clear(); + added.clear(); + + for (Iterator iter =3D super.iterator(); iter.hasNext();) { + removed.add(iter.next()); + } + + super.clear(); + + // Apply updates + this.trackUpdates(true); + + } =20 +=20 + /** + * Creates and returns a copy of this object. + * =20 + *

Mutable Second Class Objects are required to provide a public + * clone method in order to allow for copying PersistenceCapable + * objects. In contrast to Object.clone(), this method must not throw a + * CloneNotSupportedException. + */ =20 + public Object clone() { + SCOHelper.debug(_HashSet, "clone"); // NOI18N + + Object obj =3D super.clone(); + if (obj instanceof SCO) { + ((SCO)obj).unsetOwner(owner, fieldNumber); + } + return obj; + } + + /** + * @see SCOCollection#reset() + */ =20 + public void reset() { + added.clear(); + removed.clear(); + frozenElements =3D null; + } + + /** + * @see SCOCollection#addInternal(Object o) + */ =20 + public void addInternal(Object o) { + super.add(o); + } + + + /** + * @see SCOCollection#addAllInternal(Collection c) + */ =20 + public void addAllInternal(Collection c) { + for (Iterator iter =3D c.iterator(); iter.hasNext();) { + super.add(iter.next()); + } + } =20 + + /**=20 + * @see SCOCollection#getAdded() + */ =20 + public Collection getAdded() {=20 + return (Collection)added;=20 + } =20 +=20 + /** =20 + * @see SCOCollection#getRemoved() + */ =20 + public Collection getRemoved() { =20 + return (Collection)removed; =20 + } =20 + + + /**=20 + * @see SCOCollection#clearInternal() + */ =20 + public void clearInternal() { =20 + super.clear();=20 + this.reset();=20 + } =20 + + /** + * @see SCOCollection#removeInternal(Object o) + */ =20 + public void removeInternal(Object o) { + super.remove(o); + } + + /** + * @see SCO#unsetOwner(Object owner, int fieldNumber) + */ + public void unsetOwner(Object owner, int fieldNumber) {=20 + // Unset only if owner and fieldNumber match. + if (this.owner =3D=3D owner && this.fieldNumber =3D=3D fieldNumber= ) { + this.owner =3D null;=20 + this.fieldNumber =3D -1; + } + } + + /** + * @see SCO#setOwner (Object owner, int fieldNumber) + */ + public void setOwner (Object owner, int fieldNumber) { + // Set only if it was not set before. + if (this.owner =3D=3D null && owner instanceof StateManagerInterna= l) { + this.owner =3D (StateManagerInternal)owner; =20 + this.fieldNumber =3D fieldNumber; + } + } + + /**=20 + * @see SCO#getOwner() + */ =20 + public Object getOwner() { =20 + return SCOHelper.getOwner(owner); + }=20 +=20 + /** =20 + * @see SCO#getFieldName() + */ =20 + public String getFieldName() { + return SCOHelper.getFieldName(owner, fieldNumber); =20 + } + + /** + * Notify StateManager to mark field as dirty. + */ + private void makeDirty() { + if (owner !=3D null) { + owner.makeDirty(fieldNumber); // + } + } =20 + /** + * Notify StateManager to process the changes. + */ =20 + private void trackUpdates(boolean modified) { + if (modified && owner !=3D null) { + owner.trackUpdates(fieldNumber, this); + } + } + + /** + * @see SCOCollection#getElementType() { + */ =20 + public Class getElementType() { + return elementType; + } + + /** + * @see SCOCollection#allowNulls() { + */ =20 + public boolean allowNulls() { + return allowNulls; + } + + public boolean contains(Object o) { + thaw(); + return super.contains(o); + } + =20 + public boolean containsAll(Collection c) { + thaw(); + return super.containsAll(c); + } + =20 + public boolean isEmpty() { + thaw(); + return super.isEmpty(); + } + =20 + public Iterator iterator() { + thaw(); + return super.iterator(); + } + =20 + public int size() { + thaw(); + return super.size(); + } + =20 + public boolean equals(Object o) { + thaw(); + return super.equals(o); + } + =20 + public int hashCode() { + thaw(); + return super.hashCode(); + } + =20 + public String toString() { + thaw(); + return super.toString(); + } + =20 + public Object[] toArray() { + thaw(); + return super.toArray(); + } + =20 + public Object[] toArray(Object[] a) { + thaw(); + return super.toArray(a); + } + =20 + /** Returns the frozen state of this set. + * @since 1.0.1 + */ + private boolean isFrozen() { + return frozenElements !=3D null; + } + =20 + /** Returns the frozen contents of this Collection, if this Collection + * is implicitly user-orderable. If the collection is not frozen alre= ady, + * freeze it first. + * @since 1.0.1 + * @return the frozen elements of this collection. + */ + private Object[] getFrozen() { + if (!isFrozen()) { + frozenElements =3D Freezer.freeze(this, super.size()); + } + return frozenElements; + } + =20 + /** Set the contents of this Collection from the frozen elements, if t= his Collection + * is implicitly user-orderable. + * @since 1.0.1 + * @param elements the frozen elements of this set. + */ + public void setFrozen(Object[] elements) { + frozenElements =3D elements; + } + =20 + /** + * Thaw the frozen elements of this collection. If the elements are fr= ozen, + * retrieve them from the datastore and internally add them. Then reset + * the frozen state since they're not frozen any more. + * @since 1.0.1 + */ + private void thaw() { + if (isFrozen()) { + frozenElements =3D Freezer.thaw(this, owner, frozenElements); + } + } + =20 + /** Create a new iterator over the frozen elements without thawing. + * @since 1.0.1 + * @return the frozen iterator. + */ =20 + public Iterator frozenIterator() { + return Freezer.createFrozenIterator(getFrozen()); + } + =20 + /** Create an iterator regardless whether the collection is frozen.=20 + * If frozen, don't thaw the collection, but get a frozen iterator.=20 + * If thawed, get a regular iterator. + * @since 1.0.1 + * @return the iterator. + */ + public Iterator eitherIterator() { + if (isFrozen()) { + return frozenIterator(); + } else { + return super.iterator(); + } + } + =20 +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/Hashtable.= java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/ap= ache/jdo/impl/sco/Hashtable.java?view=3Dauto&rev=3D158176 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/Hashtable.jav= a (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/sco/Hashtable.jav= a Fri Mar 18 17:02:29 2005 @@ -0,0 +1,614 @@ +/* + * Copyright 2005 The Apache Software Foundation. + *=20 + * 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=20 + *=20 + * http://www.apache.org/licenses/LICENSE-2.0 + *=20 + * Unless required by applicable law or agreed to in writing, software=20 + * distributed under the License is distributed on an "AS IS" BASIS,=20 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied= .=20 + * See the License for the specific language governing permissions and=20 + * limitations under the License. + */ + +/*=20 + * sco.Hashtable.java + */ +=20 +package org.apache.jdo.impl.sco; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Enumeration; + +import org.apache.jdo.sco.SCO; +import org.apache.jdo.sco.SCOMap; +import org.apache.jdo.state.StateManagerInternal; +import org.apache.jdo.util.I18NHelper; + + +/** + * A mutable 2nd class object that represents Hashtable. + * @author Marina Vatkina + * @version 1.0.1 + * @see java.util.Hashtable + */ +public class Hashtable extends java.util.Hashtable implements SCOMap { + + private transient StateManagerInternal owner; + + private transient int fieldNumber =3D -1; + + private transient Class keyType; + + private transient Class valueType; + + private transient boolean allowNulls; + + private transient java.util.ArrayList addedKeys =3D new java.util.Arra= yList(); + private transient java.util.ArrayList addedValues =3D new java.util.Ar= rayList(); + + private transient java.util.ArrayList removedKeys =3D new java.util.Ar= rayList(); + private transient java.util.ArrayList removedValues =3D new java.util.= ArrayList(); + + private transient Map.Entry[] frozenEntries =3D null; + /**=20 + * I18N message handler=20 + */=20 + private final static I18NHelper msg =3D I18NHelper.getInstance(=20 + "org.apache.jdo.impl.sco.Bundle"); // NOI18N + + private final static String _Hashtable =3D "Hashtable"; // NOI18N + + /** =20 + * Creates a new empty Hashtable object. + * =20 + * @param keyType the type of the keys allowed. + * @param valueType the type of the values allowed. + * @param allowNulls true if nulls are allowed. + * @see java.util.Hashtable + */ =20 + public Hashtable(Class keyType, Class valueType, boolean allowNulls) { + super();=20 + this.keyType =3D keyType; + this.valueType =3D valueType; + this.allowNulls =3D allowNulls; + }=20 + + /** =20 + * Creates a new empty Hashtable object that has=20 + * the specified initial capacity. + * =20 + * @param keyType the type of the keys allowed. + * @param valueType the type of the values allowed. + * @param allowNulls true if nulls are allowed + * @param initialCapacity the initial capacity of the hash map.=20 + * @throws IllegalArgumentException if the initial capacity is less + * than zero. + * @see java.util.Hashtable + */ =20 + public Hashtable(Class keyType, Class valueType, boolean allowNulls,=20 + int initialCapacity) { + + super(initialCapacity);=20 + this.keyType =3D keyType; + this.valueType =3D valueType; + this.allowNulls =3D allowNulls; + }=20 + + /** =20 + * Creates a new empty Hashtable object that has=20 + * the specified initial capacity.. + * =20 + * @param keyType the type of the keys allowed. + * @param valueType the type of the values allowed. + * @param allowNulls true if nulls are allowed + * @param initialCapacity the initial capacity of the hash map.=20 + * @param loadFactor the load factor of the hash map. + * @throws IllegalArgumentException if the initial capacity is less + * than zero. + * @see java.util.Hashtable + */ =20 + public Hashtable(Class keyType, Class valueType, boolean allowNulls,=20 + int initialCapacity, float loadFactor) { + + super(initialCapacity, loadFactor);=20 + this.keyType =3D keyType; + this.valueType =3D valueType; + this.allowNulls =3D allowNulls; + }=20 + + // -------------------------Public Methods------------------ + + /** + * Associates the specified value with the specified key in this map. + * If the map previously contained a mapping for this key, the old + * value is replaced. + * + * @param key key with which the specified value is to be associated. + * @param value value to be associated with the specified key. + * @return previous value associated with specified key, or null + * if there was no mapping for key. A null return can + * also indicate that the Hashtable previously associated + * null with the specified key. + * @see java.util.Hashtable + */ + public Object put(Object key, Object value) { + SCOHelper.debug(_Hashtable, "put"); // NOI18N + + // Check both the key and the value: + Throwable[] err =3D new Throwable[2]; + int l =3D 0; + + try { + SCOHelper.assertNullKeysAllowed(key, allowNulls); + SCOHelper.assertKeyType(key, keyType); + } catch (Throwable ex) { + err[l++] =3D ex; + } + try { + SCOHelper.assertNullValuesAllowed(value, allowNulls); + SCOHelper.assertValueType(value, valueType); + } catch (Throwable ex) { + err[l++] =3D ex; + } + + SCOHelper.validateResult(l, err); + + // Mark the field as dirty + this.makeDirty(); + + thaw(); + Object o =3D process(key, value); + + // Apply updates + this.trackUpdates(true); + + return o; + } =20 + + /** + * Copies all of the mappings from the specified map to this one. + * + * These mappings replace any mappings that this map had for any of the + * keys currently in the specified Map. + * + * @param t Mappings to be stored in this map. + * @see java.util.HashSet + */ =20 + public void putAll(Map t) { + SCOHelper.debug(_Hashtable, "putAll"); // NOI18N + + // iterate the collection and make a list of wrong elements. + Throwable[] err =3D new Throwable[2*t.size()]; + int l =3D 0; + + Iterator i =3D t.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry e =3D (Map.Entry) i.next(); + Object k =3D e.getKey(); + Object v =3D e.getValue(); + + // Check both the key and the value: + try { + SCOHelper.assertNullKeysAllowed(k, allowNulls); + SCOHelper.assertKeyType(k, keyType); + } catch (Throwable ex) { + err[l++] =3D ex; + } + try { + SCOHelper.assertNullValuesAllowed(v, allowNulls); + SCOHelper.assertValueType(v, valueType); + } catch (Throwable ex) { + err[l++] =3D ex; + } + } + SCOHelper.validateResult(l, err); + + boolean modified =3D false; + // Mark the field as dirty + this.makeDirty(); + + thaw(); + for (i =3D t.entrySet().iterator(); i.hasNext();) { + Map.Entry e =3D (Map.Entry) i.next();=20 + process(e.getKey(), e.getValue()); + } + + // Apply updates + this.trackUpdates(true); + }=20 + + /** + * Removes the mapping for this key from this map if present. + * + * @param key key whose mapping is to be removed from the map. + * @return previous value associated with specified key, or null + * if there was no mapping for key. A null return can + * also indicate that the map previously associated null + * with the specified key. + * @see java.util.HashSet + */ =20 + public Object remove(Object key) { + SCOHelper.debug(_Hashtable, "remove"); // NOI18N + + // Mark the field as dirty + this.makeDirty(); + + thaw(); + boolean removed =3D false; + + // Nothing is added to the removed collections if the key has not + // been in the map before: + if (super.containsKey(key)) { + if (addedKeys.remove(key) =3D=3D false) { + removedKeys.add(key); + } + removed =3D true; + } + + Object o =3D super.remove(key); + + // Remove old value if there was one: + if (removed && addedValues.remove(o) =3D=3D false) { + removedValues.add(o); + } + + // Apply updates + this.trackUpdates(removed); + + return o; + } =20 + + + /** + * Removes all of the elements from this map. + * @see java.util.HashMap + */ =20 + public void clear() { + SCOHelper.debug(_Hashtable, "clear"); // NOI18N + + // Mark the field as dirty + this.makeDirty(); + + thaw(); + for (Iterator i =3D super.entrySet().iterator(); i.hasNext();) { + Map.Entry e =3D (Map.Entry) i.next(); + removedKeys.add(e.getKey()); + removedValues.add(e.getValue()); + } + + super.clear(); + addedKeys.clear(); + addedValues.clear(); + + // Apply updates + this.trackUpdates(true); + + } =20 +=20 + /** Creates and returns a copy of this object. + * + *

Mutable Second Class Objects are required to provide a public + * clone method in order to allow for copying PersistenceCapable + * objects. In contrast to Object.clone(), this method must not throw a + * CloneNotSupportedException. + * @return the clone. + */ =20 + public Object clone() { + SCOHelper.debug(_Hashtable, "clone"); // NOI18N + + Object obj =3D super.clone(); + if (obj instanceof SCO) { + ((SCO)obj).unsetOwner(owner, fieldNumber); + } + return obj; + } + + /** These methods need to thaw the map before performing the operation. + */ + public boolean containsKey(Object key) { + thaw(); + return super.containsKey(key); + } + public boolean containsValue(Object value) { + thaw(); + return super.containsValue(value); + } + public Set entrySet() { + thaw(); + return super.entrySet(); + } + public boolean equals(Object o) { + thaw(); + return super.equals(o); + } + public Object get(Object key) { + thaw(); + return super.get(key); + } + public int hashCode() { + thaw(); + return super.hashCode(); + } + public boolean isEmpty() { + thaw(); + return super.isEmpty(); + } + public Set keySet() { + thaw(); + return super.keySet(); + } + public int size() { + thaw(); + return super.size(); + } + public Collection values() { + thaw(); + return super.values(); + } + public String toString() { + thaw(); + return super.toString(); + } + public boolean contains(Object value) { + thaw(); + return super.contains(value); + } + public Enumeration elements() { + thaw(); + return super.elements(); + } + public Enumeration keys() { + thaw(); + return super.keys(); + } + // Note, no need to redefine the protected method rehash.=20 + // Actually redefining rehash lead to an endless recursion. + =20 + /** + * @see SCOMap#reset() + */ =20 + public void reset() { + addedKeys.clear(); + addedValues.clear(); + removedKeys.clear(); + removedValues.clear(); + frozenEntries =3D null; + } + + /** + * @see SCOMap#putInternal(Object key, Object value) + */ =20 + public void putInternal(Object key, Object value) { + super.put(key, value); + } + + + /** + * @see SCOMap#putAllInternal(Map t) + */ =20 + public void putAllInternal(Map t) { + for (Iterator i =3D t.entrySet().iterator(); i.hasNext();) { + Map.Entry e =3D (Map.Entry) i.next(); + super.put(e.getKey(), e.getValue()); + } + } =20 + + /**=20 + * @see SCOMap#getAddedKeys() + */ =20 + public Collection getAddedKeys() {=20 + return (Collection)addedKeys;=20 + } =20 +=20 + /**=20 + * @see SCOMap#getAddedValues() + */ =20 + public Collection getAddedValues() {=20 + return (Collection)addedValues;=20 + } =20 +=20 + /** =20 + * @see SCOMap#getRemovedKeys() + */ =20 + public Collection getRemovedKeys() { =20 + return (Collection)removedKeys; =20 + } =20 + + /** =20 + * @see SCOMap#getRemovedValues() + */ =20 + public Collection getRemovedValues() { =20 + return (Collection)removedValues; =20 + } =20 + + /**=20 + * @see SCOMap#clearInternal() + */ =20 + public void clearInternal() { =20 + super.clear();=20 + this.reset();=20 + } =20 + + /** + * @see SCOMap#removeInternal(Object key) + */ =20 + public void removeInternal(Object key) { + super.remove(key); + } + + /** + * @see SCO#unsetOwner(Object owner, int fieldNumber) + */ + public void unsetOwner(Object owner, int fieldNumber) {=20 + // Unset only if owner and fieldNumber match. + if (this.owner =3D=3D owner && this.fieldNumber =3D=3D fieldNumber= ) { + this.owner =3D null;=20 + this.fieldNumber =3D -1; + } + } + + /** + * @see SCO#setOwner (Object owner, int fieldNumber) + */ + public void setOwner (Object owner, int fieldNumber) { + // Set only if it was not set before. + if (this.owner =3D=3D null && owner instanceof StateManagerInterna= l) { + this.owner =3D (StateManagerInternal)owner; =20 + this.fieldNumber =3D fieldNumber; + } + } + + /**=20 + * @see SCO#getOwner() + */ =20 + public Object getOwner() { =20 + return SCOHelper.getOwner(owner); + }=20 +=20 + /** =20 + * @see SCO#getFieldName() + */ =20 + public String getFieldName() { + return SCOHelper.getFieldName(owner, fieldNumber); =20 + } + + /** + * Notify StateManager to mark field as dirty. + */ + private void makeDirty() { + if (owner !=3D null) { + owner.makeDirty(fieldNumber); // + } + } =20 + /** + * Notify StateManager to process the changes. + */ =20 + private void trackUpdates(boolean modified) { + if (modified && owner !=3D null) { + owner.trackUpdates(fieldNumber, this); + } + } + + /** + * @see SCOMap#getKeyType() { + */ =20 + public Class getKeyType() { + return keyType; + } + + /** + * @see SCOMap#getValueType() { + */ =20 + public Class getValueType() { + return valueType; + } + + /** + * @see SCOMap#allowNulls() { + */ =20 + public boolean allowNulls() { + return allowNulls; + } + + /** + * Processes single put operation in this map. + * @param key key with which the specified value is to be associated. + * @param value value to be associated with the specified key. + * @return previous value associated with specified key, or null + * if there was no mapping for key.=20 + */ + private Object process(Object key, Object value) { + // Key is added to the addedKeys collection only if it has not + // been in the map before: + if (!super.containsKey(key)) { + if (removedKeys.remove(key) =3D=3D false) { + addedKeys.add(key); + } + } + + Object o =3D super.put(key, value); + + // Remove old value: + if (addedValues.remove(o) =3D=3D false) { + removedValues.add(o); + } + + // Add new value: + if (removedValues.remove(value) =3D=3D false) { + addedValues.add(value); + } + + return o; + } + =20 + /** Returns the frozen state of this Map. + * @since 1.0.1 + * @return the frozen state. + */ + private boolean isFrozen() { + return frozenEntries !=3D null; + } + =20 + /** Returns the frozen contents of this Map as a Map.Entry[]. + * @since 1.0.1 + * @return the frozen entries. + */ + private Map.Entry[] getFrozen() { + if (!isFrozen()) { + frozenEntries =3D Freezer.freeze(this, super.size()); + } + return frozenEntries; + } + =20 + /** Set the contents of this Map from the frozen entries. + * @since 1.0.1 + * @param entries the frozen entries. + */ + public void setFrozen(Map.Entry[] entries) { + frozenEntries =3D entries; + } + =20 + /** Get an iterator regardless of whether the map is frozen. + * If frozen, get a frozen iterator. + * If thawed, get a regular iterator. + * @since 1.0.1 + * @return the iterator. + */ + public Iterator eitherIterator() { + if (isFrozen()) { + return frozenIterator(); + } else { + return super.entrySet().iterator(); + } + } + =20 + /** Get an iterator over the frozen elements of this map. This allows + * iteration of the elements without thawing them, as is needed for + * transcription. + * @since 1.0.1 + * @return the iterator. + */ + public Iterator frozenIterator() { + return Freezer.createFrozenIterator(getFrozen()); + } + =20 + /** + * Thaw the frozen elements of this map. If the elements are frozen, + * retrieve them from the datastore and internally add them. Then reset + * the frozen state since they're not frozen any more. + * @since 1.0.1 + */ + private void thaw() { + if (isFrozen()) { + frozenEntries =3D Freezer.thaw(this, owner, frozenEntries); + } + } + =20 +}