001: /*
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * Initial developer(s): Sebastien Chassande
022: * Contributor(s):
023: *
024: * --------------------------------------------------------------------------
025: * $Id: GenClassImpl.java 10146 2007-04-05 06:56:36Z durieuxp $
026: * --------------------------------------------------------------------------
027: */
028: package org.objectweb.jonas_ejb.container.jorm;
029:
030: import java.util.ArrayList;
031: import java.util.Arrays;
032: import java.util.Iterator;
033: import java.util.NoSuchElementException;
034: import javax.ejb.EJBException;
035: import org.objectweb.jonas_ejb.container.JEntityFactory;
036: import org.objectweb.jonas_ejb.container.TraceEjb;
037: import org.objectweb.jorm.api.PBinding;
038: import org.objectweb.jorm.api.PClassMapping;
039: import org.objectweb.jorm.api.PException;
040: import org.objectweb.jorm.api.PExceptionProtocol;
041: import org.objectweb.jorm.api.PGenClassAccessor;
042: import org.objectweb.jorm.api.PIndexedElem;
043: import org.objectweb.jorm.naming.api.PBinder;
044: import org.objectweb.jorm.naming.api.PName;
045: import org.objectweb.jorm.naming.api.PNameManager;
046: import org.objectweb.jorm.naming.api.PExceptionNaming;
047: import org.objectweb.util.monolog.api.BasicLevel;
048:
049: /**
050: * This class is a basic implementation of the PGenClassAccessor interface
051: * usefull for the multivalued relation.
052: *
053: * @author S.Chassande-Barrioz
054: */
055: public abstract class GenClassImpl implements PGenClassAccessor {
056:
057: /**
058: * All elements of the gen class (unmodified, modified, created, deleted)
059: * This ArrayList contains GenClassElem objects
060: */
061: protected ArrayList pIndexedElems = null;
062:
063: /**
064: * This array represents the distance between two deleted elements in the
065: * pIndexedElems arraylist. O means there is no element in the middle. The
066: * first element is in fact the quantity of existing element before the
067: * first deleted element.
068: * The size of this array is also the quantity of deleted elements. Then if
069: * the array is empty there is no deleted elements.
070: *
071: * for example: if this fields is equals to [3, 2] then the elements whith
072: * the index 3 and 6 are marked as deleted: [a, b, c, D, d, e, D, ....]
073: *
074: * This array is useless to convert an index valid inside the user/virtual
075: * collection to an index valid inside the 'pIndexedElems' ArrayList.
076: */
077: protected int[] deletedLength = null;
078:
079: /**
080: * This is the size of the relation. This field is equal to the
081: * pIndexedElems size minus the quantity of deleted element. The value is
082: * always right and is updated during each delete or create action.
083: */
084: protected int size;
085:
086: /**
087: * true when GenClass has been modified and must be written
088: */
089: protected boolean isModified = false;
090:
091: /**
092: * true when we want use select .. for ..update
093: */
094: protected boolean selectForUpdate = false;
095:
096: /**
097: * This field references the PClassMapping which manages the persistency of
098: * this GenClass. (xxxGCM.java)
099: * This is actually a GenClassMapping, returned by the bean Factory (PClassMapping)
100: */
101: protected PClassMapping gcm = null;
102:
103: protected org.objectweb.jonas_ejb.container.JEntityContext ectx = null;
104:
105: /**
106: * This fields is the PBinding associated to this instance.
107: */
108: protected PBinding pb = null;
109:
110: /**
111: * All the actions are send to this listener.
112: * There is 1 Listener for each GenClass, i.e. for each
113: * multi-valued CMR field. (See JEntityCmp2.vm)
114: * Used mainly for coherence.
115: */
116: protected GenClassListener listener = null;
117:
118: /**
119: * A GenClassImpl object is created for each Multi-Valued CMR field
120: * See JEntityCmp2.vm
121: * It starts with an empty list.
122: */
123: public GenClassImpl() {
124: pIndexedElems = new ArrayList(0);
125: size = 0;
126: deletedLength = new int[0];
127: isModified = false;
128: }
129:
130: /**
131: * Set the PClassMapping for this GenClass. It's needed to get
132: * Connections for read and write operations.
133: * Used by JEntityCmp2.vm
134: * @param gcm the PClassMapping
135: */
136: public void setPClassMapping(PClassMapping gcm) {
137: this .gcm = gcm;
138: }
139:
140: /**
141: * Set the EntityContext needed for setDirty calls
142: */
143: public void setEntityContext(
144: org.objectweb.jonas_ejb.container.JEntityContext ectx) {
145: this .ectx = ectx;
146: selectForUpdate = ectx.getEntityFactory().getSelectForUpdate();
147: }
148:
149: /**
150: * reset the GenClass to its initial state.
151: */
152: public void reset() {
153: pIndexedElems.clear();
154: size = 0;
155: deletedLength = new int[0];
156: isModified = false;
157: }
158:
159: /**
160: * Set the PBinding that will be used for this GenClass
161: * Used by JEntityCmp2.vm
162: * @param pb The PBinding for this GenClass
163: */
164: public void setPBinding(PBinding pb) {
165: this .pb = pb;
166: }
167:
168: /**
169: * Get the PBinding used for this GenClass
170: * Used by JEntityCmp2.vm
171: * @return The PBinding for this GenClass
172: */
173: public PBinding gcGetPBinding() {
174: return pb;
175: }
176:
177: public void setListener(GenClassListener gcl) {
178: listener = gcl;
179: }
180:
181: public GenClassListener getListener() {
182: return listener;
183: }
184:
185: // IMPLEMENTATION OF THE Persitence lyfe cycle //
186: //---------------------------------------------//
187:
188: public boolean gcIsModified() {
189: return isModified;
190: }
191:
192: /**
193: * It loads the data of the gen class.
194: * @param pn is the PName of the genclass
195: * @param connection is a connection to access to the support. If it is null
196: * a connection is asked to the mapper and closed after its use.
197: */
198: public void read(PName pn, Object connection, Object tx)
199: throws PException {
200: if (pn == null || pn.isNull()) {
201: TraceEjb.genclass.log(BasicLevel.DEBUG, "null pn");
202: reset();
203: return;
204: }
205: if (pb == null) {
206: throw new PExceptionProtocol(
207: "Impossible to read a persitent object withoout PBinding");
208: }
209: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
210: TraceEjb.genclass.log(BasicLevel.DEBUG, "PName=" + pn);
211: }
212:
213: // Allocate a connection if needed
214: Object conn = (connection == null ? gcm.getPMapper()
215: .getConnection() : connection);
216: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
217: TraceEjb.genclass
218: .log(BasicLevel.DEBUG, "Load the genclass");
219: }
220: pb.bind(pn);
221: pb.read(conn, this , tx, selectForUpdate);
222: //pb.unbind();
223: isModified = false;
224:
225: // close the connection opened localy
226: if (connection == null) {
227: gcm.getPMapper().closeConnection(conn);
228: }
229: }
230:
231: /**
232: * It writes the data of the gen class if it was modified (see the field
233: * isModified).
234: * @param pn is the PName of the genclass
235: * @param connection is a connection to access to the support. If it is null
236: * a connection is asked to the mapper and closed after its use.
237: */
238: public void write(PName pn, Object connection) throws PException {
239: //TraceEjb.genclass.log(BasicLevel.DEBUG, "begin"); printState();
240: if (pn == null || pn.isNull()) {
241: throw new PExceptionProtocol(
242: "Impossible to write a persitent object with a null PName: "
243: + pn);
244: }
245: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
246: TraceEjb.genclass.log(BasicLevel.DEBUG, "");
247: }
248: if (isModified) {
249: // Allocate a connection if needed
250: Object conn = (connection == null ? gcm.getPMapper()
251: .getConnection() : connection);
252:
253: if (pb == null) {
254: throw new PExceptionProtocol(
255: "Impossible to write a persitent object without PBinding");
256: }
257: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
258: TraceEjb.genclass.log(BasicLevel.DEBUG,
259: "Store the genclass " + pn);
260: }
261: pb.bind(pn);
262: pb.write(conn, this );
263: //pb.unbind();
264:
265: // The data are written on the support, then the memory instance is
266: // up to date. The element marked as removed must really be removed
267: // from the list.
268: isModified = false;
269: int pos = 0;
270: for (int i = 0; i < deletedLength.length; i++) {
271: pos += deletedLength[i];
272: pIndexedElems.remove(pos);
273: }
274: deletedLength = new int[0];
275: for (Iterator it = pIndexedElems.iterator(); it.hasNext();) {
276: GenClassElement gce = (GenClassElement) it.next();
277: gce.status = PIndexedElem.ELEM_UNMODIFIED;
278: gce.hasBeenCreated = false;
279: }
280:
281: // close the connection opened localy
282: if (connection == null) {
283: gcm.getPMapper().closeConnection(conn);
284: }
285: }
286: //TraceEjb.genclass.log(BasicLevel.DEBUG, "end"); printState();
287: }
288:
289: // IMPLEMENTATION OF THE PAccessor INTERFACE //
290: //-------------------------------------------//
291:
292: /**
293: * In most of cases this class is extented to personalize to a collection
294: * type. Then an instance of this class is often the real collection.
295: */
296: public Object getMemoryInstance() {
297: return this ;
298: }
299:
300: // IMPLEMENTATION OF THE PGenClassAccessor INTERFACE //
301: //---------------------------------------------------//
302:
303: /**
304: * It adds the elements in the list. This method is used by the PBinding to
305: * load the data.
306: * The elements is added at the end of the 'pIndexedElems' then
307: * 'deletedLength' is not impacted.
308: */
309: public void paAdd(PIndexedElem elem, Object conn) throws PException {
310: pIndexedElems.add(elem);
311: size++;
312: //TraceEjb.genclass.log(BasicLevel.DEBUG, "printState"); printState();
313: }
314:
315: /**
316: * The default implementation of the PIndexedElem is GenClassElement.
317: * This method may be redefined for different GenClass implementations.
318: */
319: public PIndexedElem createPIndexedElem() {
320: return new GenClassElement(this );
321: }
322:
323: /**
324: * This implementation is able to isolate the modification by element of the
325: * gen class.
326: */
327: public boolean paDeltaSupported() {
328: return true;
329: }
330:
331: /**
332: * This method is used by the PBinding to allocated data structure during a
333: * write operation. The returned size must then contains also the deleted
334: * elements.
335: */
336: public int paGetNbElem() {
337: return pIndexedElems.size();
338: }
339:
340: /**
341: * This method is used by the PBinding to fetch all PIndexedElem.
342: */
343: public Iterator paIterator() {
344: return pIndexedElems.iterator();
345: }
346:
347: /**
348: * This method is call in first during a read operation in order to indicate
349: * the size of the gen class. if the value is equals to -1 then that means
350: * the data support is unable to known the size of the relation in advance.
351: * Then the previous size is kept. Otherwise the list is initialized to the
352: * specified size.
353: */
354: public void paSetNbElem(int nbelem) {
355: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
356: TraceEjb.genclass
357: .log(BasicLevel.DEBUG, "nbElem: " + nbelem);
358: }
359: if (nbelem == -1) {
360: pIndexedElems.clear();
361: } else {
362: pIndexedElems = new ArrayList(nbelem);
363: }
364: deletedLength = new int[0];
365: size = 0;
366: }
367:
368: // METHODS USABLE FOR THE SUB CLASS //
369: //----------------------------------//
370:
371: /**
372: * This method calculates the real index of an element.
373: * @param idx the index valid in the colllection
374: * @return an index valid into the 'pIndexedElems' ArrayList.
375: */
376: protected int gcGetRealIndex(int idx) {
377: int a = idx;
378: int i = 0;
379: while (i < deletedLength.length && (a -= deletedLength[i]) >= 0) {
380: i++;
381: }
382: return idx + i;
383: }
384:
385: /**
386: * This method add a new element in the collection. A PIndexedElem is built
387: * via the 'createPIndexedElem' method. Then this instance is initialized
388: * with the element and the staus specified by the parameter 'status'.
389: * Before added the element, a checking is done. If the element was
390: * previously removed in the same transaction then the status of the
391: * removed element is changed to ELEM_UNMODIFIED. This optimization avoid
392: * two useless I/O.
393: *
394: * @param element the object to add
395: * @param callListener indicates if the gen class listener must be call
396: * about this action
397: */
398: protected void gcAdd(PObject element, boolean callListener) {
399: //TraceEjb.genclass.log(BasicLevel.DEBUG, "printState"); printState();
400: int pos = 0;
401: GenClassElement gce = null;
402: int i = 0;
403:
404: // call setDirty before, because if there is a conflict, we must
405: // not update the collection.
406: ectx.setDirty(true);
407:
408: while (i < deletedLength.length) {
409: pos += deletedLength[i];
410: gce = (GenClassElement) pIndexedElems.get(pos);
411: if (gce.value == element) {
412: break;
413: }
414: pos++;
415: i++;
416: }
417: if (i < deletedLength.length) {
418: if (gce.hasBeenCreated) {
419: throw new EJBException(
420: "Internal error state: A deleted element has not been created");
421: }
422: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
423: TraceEjb.genclass.log(BasicLevel.DEBUG,
424: "The element added was previously removed. i: "
425: + i + "gce.pname: " + gce.pname);
426: }
427: // The element was previously removed and added now.
428: // - the status must be change to unmodified
429: gce.status = PIndexedElem.ELEM_UNMODIFIED;
430: if (deletedLength.length == 1) {
431: deletedLength = new int[0];
432: } else {
433: int[] old = deletedLength;
434: deletedLength = new int[deletedLength.length - 1];
435: System.arraycopy(old, 0, deletedLength, 0, i);
436: if (i < deletedLength.length) {
437: System.arraycopy(old, i + 1, deletedLength, i,
438: old.length - 1 - i);
439: deletedLength[i] += old[i] + 1;
440: }
441: }
442: } else {
443: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
444: try {
445: TraceEjb.genclass
446: .log(BasicLevel.DEBUG, "Add the element '"
447: + element.getPName().encodeString());
448: } catch (PExceptionNaming pExceptionNaming) {
449: }
450: }
451: gce = (GenClassElement) createPIndexedElem();
452: gce.value = element;
453: gce.status = PIndexedElem.ELEM_CREATED;
454: gce.hasBeenCreated = true;
455: // Check if the element is legal, before added it (fix bug #300521)
456: listener.isLegalElement(gce);
457: pIndexedElems.add(gce);
458: }
459: isModified = true;
460: size++;
461: //TraceEjb.genclass.log(BasicLevel.DEBUG, "printState"); printState();
462: if (callListener) {
463: if (gce == null) {
464: TraceEjb.genclass.log(BasicLevel.ERROR, "null gce");
465: return;
466: }
467: listener.gcAdd(gce);
468: }
469: }
470:
471: /**
472: * It removes the first occurence of an element from the relation.
473: * @param element an element which must be removed
474: * @param callListener indicates if the gen class listener must be call
475: * about this action
476: * @return the remove element
477: */
478: protected Object gcRemove(Object element, boolean callListener)
479: throws PException {
480: //TraceEjb.genclass.log(BasicLevel.DEBUG, "printState"); printState();
481: GenClassElement gce = null;
482: boolean found = false;
483: int b = 0; //The number of existing element since the last deleted element
484: int i = 0; // Quantity of deleted elements before
485: int a;
486: for (a = 0; a < pIndexedElems.size() && !found; a++) {
487: gce = (GenClassElement) pIndexedElems.get(a);
488: if (gce.status == PIndexedElem.ELEM_DELETED) {
489: i++;
490: b = 0;
491: continue; //Do not search the element among the deleted elements
492: }
493: b++;
494: if (gce.pname != null && gce.value == null) {
495: gce.value = gcDeref(gce.pname);
496: }
497: found = (element == null && gce.value == null)
498: || (element != null && element.equals(gce.value));
499: }
500: if (!found) {
501: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
502: TraceEjb.genclass.log(BasicLevel.DEBUG,
503: "Nothing to remove");
504: }
505: return null;
506: }
507:
508: // call setDirty before, because if there is a conflict, we must
509: // not update the collection.
510: ectx.setDirty(true);
511:
512: // call listener before removing field
513: if (callListener) {
514: listener.gcRemove(gce, false);
515: }
516: if (gce.hasBeenCreated) {
517: // Remove the element permanantly
518: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
519: TraceEjb.genclass.log(BasicLevel.DEBUG,
520: "Remove permanantly the element");
521: }
522: pIndexedElems.remove(a - 1);
523: if (i < deletedLength.length) {
524: //decrease the number of existing element until the next deletec
525: // element.
526: deletedLength[i]--;
527: }
528: } else {
529: //Mark the element as removed
530: gce.status = PIndexedElem.ELEM_DELETED;
531:
532: // add the entry in the deletedLength array
533: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
534: TraceEjb.genclass.log(BasicLevel.DEBUG,
535: "Add the entry in the deletedLength array: i:"
536: + i + " / b:" + b + ")");
537: }
538: int[] old = deletedLength;
539: deletedLength = new int[old.length + 1];
540: System.arraycopy(old, 0, deletedLength, 0, i);
541: deletedLength[i] = b - 1;
542: if (i < old.length) {
543: deletedLength[i + 1] = old[i] - b;
544: System.arraycopy(old, i + 1, deletedLength, i + 2,
545: old.length - i - 1);
546: }
547: }
548: isModified = true;
549: size--;
550: //TraceEjb.genclass.log(BasicLevel.DEBUG, "printState"); printState();
551: return gce.value;
552: }
553:
554: private static String toString(int[] t) {
555: StringBuffer sb = new StringBuffer("[");
556: for (int i = 0; i < t.length;) {
557: sb.append(t[i]);
558: i++;
559: if (i < t.length) {
560: sb.append(',');
561: }
562: }
563: sb.append(']');
564: return sb.toString();
565: }
566:
567: public int gcGetSize() {
568: return size;
569: }
570:
571: protected boolean gcContains(PObject element, Object connection)
572: throws PException {
573: //Open a connection if the parameter is null
574: Object conn = (connection == null ? gcm.getPMapper()
575: .getConnection() : connection);
576:
577: boolean result = false;
578: for (Iterator it = gcIterator(conn); !result && it.hasNext();) {
579: Object o = it.next();
580: result = (element == null && o == null)
581: || (element != null && element.equals(o));
582: }
583:
584: if (connection == null) {
585: // close the localy opened connection
586: gcm.getPMapper().closeConnection(conn);
587: }
588: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
589: try {
590: TraceEjb.genclass.log(BasicLevel.DEBUG,
591: "Looking for the element '"
592: + element.getPName().encodeString()
593: + ". return " + result);
594: } catch (PExceptionNaming pExceptionNaming) {
595: }
596: }
597: return result;
598: }
599:
600: protected Iterator gcIterator() throws PException {
601: return gcIterator(null);
602: }
603:
604: /**
605: * @param connection the connection to use during the PName resolving (if it has not
606: * already dereferenced).
607: * @return an Iterator over the virtual relations (only the existing elements).
608: */
609: protected Iterator gcIterator(Object connection) throws PException {
610: return new ElementIterator(this , connection);
611: }
612:
613: /**
614: * It dereferences an element if needed
615: * @param gce is the PIndexedElem which must be dereferenced
616: * @param connection a connection to use to resolve the PName. If this parameter is
617: * null a new connection is allocate via the mapper.
618: * This connection is closed just after its use.
619: * @return a reference to the bean (The local interface in fact).
620: */
621: protected PObject gcGetElement(GenClassElement gce,
622: Object connection) throws PException {
623: if (gce.pname != null && gce.value == null) {
624: // Dereference the PName
625: if (gce.pname == null || gce.pname.isNull()) {
626: return null;
627: }
628: PName current = gce.pname;
629:
630: // Allocate a connection if needed
631: Object conn = (connection == null ? gcm.getPMapper()
632: .getConnection() : connection);
633:
634: // Resolve the pname
635: Object res = current.resolve(conn);
636: while (res != null && res instanceof PName
637: && !res.equals(current)) {
638: current = (PName) res;
639: res = current.resolve(conn);
640: }
641:
642: // close the connection opened localy
643: if (connection == null) {
644: gcm.getPMapper().closeConnection(conn);
645: }
646:
647: // Fetch a Local Object on the factory of the referenced bean
648: gce.value = gcDeref(current);
649: }
650: return gce.value;
651: }
652:
653: /**
654: * Clear the GenClass.
655: * @param delete true if cascade delete must be checked (ONE - MANY)
656: */
657: public void gcClear(boolean delete) {
658: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
659: TraceEjb.genclass.log(BasicLevel.DEBUG, "Clear the gc");
660: }
661: //TraceEjb.genclass.log(BasicLevel.DEBUG, "begin"); printState();
662:
663: // Call listener now because ejbRemove can still access fields.
664: for (Iterator it = pIndexedElems.iterator(); it.hasNext();) {
665: GenClassElement gce = (GenClassElement) it.next();
666: if (gce.status != PIndexedElem.ELEM_DELETED) {
667: if (gce.pname != null && gce.value == null) {
668: gce.value = gcDeref(gce.pname);
669: }
670: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
671: TraceEjb.genclass.log(BasicLevel.DEBUG,
672: "listener.gcRemove");
673: }
674: listener.gcRemove(gce, delete);
675: }
676: }
677:
678: // Mark all elements as DELETED or remove the created element.
679: for (Iterator it = pIndexedElems.iterator(); it.hasNext();) {
680: GenClassElement gce = (GenClassElement) it.next();
681: if (gce.status != PIndexedElem.ELEM_DELETED) {
682: gce.status = PIndexedElem.ELEM_DELETED;
683: if (gce.hasBeenCreated) {
684: // Remove the element permanantly
685: if (TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
686: TraceEjb.genclass.log(BasicLevel.DEBUG,
687: "Clear permanantly an element");
688: }
689: it.remove();
690: }
691: }
692: }
693: // update the virtual size
694: size = 0;
695: isModified = true;
696: ectx.setDirty(true);
697:
698: // As all elements are deleted, the 'deletedLength' contains only 0
699: // numbers. The size of this array is the same as the size of the
700: // pIndexedElems list.
701: deletedLength = new int[pIndexedElems.size()];
702: Arrays.fill(deletedLength, 0);
703: //TraceEjb.genclass.log(BasicLevel.DEBUG, "end"); printState();
704:
705: }
706:
707: protected PObject gcDeref(PName pn) {
708: JEntityFactory f = (JEntityFactory) ((PBinder) pn
709: .getPNameManager()).getBinderClassMapping();
710: if (f.getLocalHome() != null) {
711: return ((PObjectHome) f.getLocalHome()).getPObject(pn);
712: } else {
713: return ((PObjectHome) f.getHome()).getPObject(pn);
714: }
715: }
716:
717: /**
718: * This method permits to find the PName of an object.
719: */
720: protected PName gcObject2ref(PObject value) throws PException {
721: if (value != null) {
722: Object conn = gcm.getPMapper().getConnection();
723: PNameManager pnm = (PNameManager) gcm.getPNameCoder();
724: PName pn = pnm.export(conn, value.getPName(), null);
725: gcm.getPMapper().closeConnection(conn);
726: return pn;
727: } else {
728: return gcm.getPNameCoder().getNull();
729: }
730: }
731:
732: public void printState() {
733: if (!TraceEjb.genclass.isLoggable(BasicLevel.DEBUG)) {
734: return;
735: }
736: TraceEjb.genclass.log(BasicLevel.DEBUG, "deletedLength: "
737: + toString(deletedLength));
738: TraceEjb.genclass.log(BasicLevel.DEBUG, "pIndexedElems.size():"
739: + pIndexedElems.size());
740: TraceEjb.genclass.log(BasicLevel.DEBUG, "isModified: "
741: + isModified);
742: TraceEjb.genclass.log(BasicLevel.DEBUG, "size:" + size);
743: int i = 0;
744: for (Iterator it = pIndexedElems.iterator(); it.hasNext();) {
745: GenClassElement gce = (GenClassElement) it.next();
746: TraceEjb.genclass.log(BasicLevel.DEBUG, "GCE:" + i
747: + " / status:" + gce.status + " / hasBeenCreated:"
748: + gce.hasBeenCreated);
749: TraceEjb.genclass.log(BasicLevel.DEBUG, "- pname:"
750: + gce.pname);
751: TraceEjb.genclass.log(BasicLevel.DEBUG, "- value:"
752: + gce.value);
753: i++;
754: }
755: }
756:
757: /**
758: * this class is an implementation of the Iterator interface which return
759: * only the existing objects. The return elements are dereferenced, then
760: * PObject instances.
761: */
762: protected class ElementIterator implements Iterator {
763:
764: private int cursor = 0;
765: private int next = 0;
766: private GenClassImpl gc;
767: private Object conn;
768:
769: public ElementIterator(GenClassImpl gc, Object connection) {
770: this .gc = gc;
771: conn = connection;
772: reset();
773: }
774:
775: public void reset() {
776: cursor = -1;
777: next = nextExist(cursor);
778: }
779:
780: private int nextExist(int pos) {
781: int tmp = pos + 1;
782: while (tmp < pIndexedElems.size()) {
783: GenClassElement gce = (GenClassElement) pIndexedElems
784: .get(tmp);
785: if (gce == null) {
786: TraceEjb.genclass.log(BasicLevel.ERROR,
787: "null GenClassElement");
788: return -1;
789: }
790: if (gce.status != PIndexedElem.ELEM_DELETED) {
791: //TraceEjb.genclass.log(BasicLevel.DEBUG, "nextExist("+pos+")="+tmp);
792: return tmp;
793: }
794: tmp++;
795: }
796: //TraceEjb.genclass.log(BasicLevel.DEBUG, "nextExist("+pos+")=-1");
797: return -1;
798: }
799:
800: // IMPLEMENTATION OF THE Iterator INTERFACE //
801: //------------------------------------------//
802:
803: /**
804: * Returns <tt>true</tt> if the iteration has more elements. (In other
805: * words, returns <tt>true</tt> if <tt>next</tt> would return an element
806: * rather than throwing an exception.)
807: *
808: * @return <tt>true</tt> if the iterator has more elements.
809: */
810: public boolean hasNext() {
811: return next != -1;
812: }
813:
814: /**
815: * Returns the next element in the interation.
816: *
817: * @return the next element in the iteration.
818: * @throws NoSuchElementException iteration has no more elements.
819: */
820: public Object next() {
821: if (next == -1) {
822: throw new NoSuchElementException();
823: }
824: cursor = next;
825: try {
826: GenClassElement gce = (GenClassElement) pIndexedElems
827: .get(cursor);
828: gc.gcGetElement(gce, conn);
829: //Caculate the next
830: next = nextExist(cursor);
831: return gce.value;
832: } catch (PException e) {
833: next = -1;
834: TraceEjb.genclass.log(BasicLevel.ERROR,
835: "Impossible to obtain value:", e);
836: throw new NoSuchElementException(
837: "Impossible to obtain value:" + e);
838: }
839: }
840:
841: public void remove() {
842: try {
843: gcRemove(
844: ((GenClassElement) pIndexedElems.get(cursor)).value,
845: true);
846: } catch (PException e) {
847: throw new UnsupportedOperationException(e.getMessage());
848: }
849: }
850: }
851: }
|