KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > snmp4j > agent > mo > DefaultMOTable


1 /*_############################################################################
2   _##
3   _## SNMP4J-Agent - DefaultMOTable.java
4   _##
5   _## Copyright (C) 2005-2007 Frank Fock (SNMP4J.org)
6   _##
7   _## Licensed under the Apache License, Version 2.0 (the "License");
8   _## you may not use this file except in compliance with the License.
9   _## You may obtain a copy of the License at
10   _##
11   _## http://www.apache.org/licenses/LICENSE-2.0
12   _##
13   _## Unless required by applicable law or agreed to in writing, software
14   _## distributed under the License is distributed on an "AS IS" BASIS,
15   _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   _## See the License for the specific language governing permissions and
17   _## limitations under the License.
18   _##
19   _##########################################################################*/

20
21
22 package org.snmp4j.agent.mo;
23
24 import java.io.*;
25 import java.util.*;
26
27 import org.snmp4j.*;
28 import org.snmp4j.agent.*;
29 import org.snmp4j.agent.io.*;
30 import org.snmp4j.agent.request.*;
31 import org.snmp4j.agent.util.*;
32 import org.snmp4j.log.*;
33 import org.snmp4j.mp.*;
34 import org.snmp4j.smi.*;
35
36 /**
37  * The <code>DefaultMOTable</code> class is the default implementation of the
38  * {@link MOTable} class. For most use cases, it is not necessary to customize
39  * this class through deriving your own sub-class. Instead, using a different
40  * {@link MOTableModel} as table data provider is sufficient.
41  * <p>
42  * The default table model can be used to hold the data of a SNMP conceptual
43  * table as real tabular data. If you want to implement a virtual table, you
44  * will have to directly implement the interfaces {@link MOTableModel} or
45  * {@link MOMutableTableModel} to access the data based on the actual view.
46  *
47  * @author Frank Fock
48  * @version 1.1
49  */

50 public class DefaultMOTable implements MOTable, MOScope,
51     SerializableManagedObject {
52
53   private static LogAdapter logger =
54       LogFactory.getLogger(DefaultMOTable.class);
55
56   private OID oid;
57   private MOTableIndex indexDef;
58   private MOColumn[] columns;
59   protected MOTableModel model;
60
61   private boolean isVolatile;
62
63   protected WeakHashMap newRows;
64   protected WeakHashMap pendingChanges;
65
66   private transient Vector moChangeListeners;
67   private transient Vector moTableRowListeners;
68
69   private transient WeakHashMap walkCache;
70
71   private static Comparator columnComparator = new Comparator() {
72
73     public int compare(Object JavaDoc o1, Object JavaDoc o2) {
74       int id1 = (o1 instanceof MOColumn) ?
75           ((MOColumn)o1).getColumnID() : ((Integer JavaDoc)o1).intValue();
76       int id2 = (o2 instanceof MOColumn) ?
77           ((MOColumn)o2).getColumnID() : ((Integer JavaDoc)o2).intValue();
78       return id1 - id2;
79     }
80   };
81
82   public DefaultMOTable(OID oid, MOTableIndex indexDef, MOColumn[] columns) {
83     this(oid, indexDef, columns, new DefaultMOMutableTableModel());
84   }
85
86   public DefaultMOTable(OID oid, MOTableIndex indexDef,
87                         MOColumn[] columns, MOTableModel model) {
88     this.oid = oid;
89     this.indexDef = indexDef;
90     this.columns = columns;
91     this.model = model;
92     registerColumns();
93   }
94
95   private void registerColumns() {
96     for (int i=0; i<columns.length; i++) {
97       columns[i].setTable(this);
98     }
99   }
100
101   public MOTableCellInfo getCellInfo(OID oid) {
102     return new CellInfo(oid);
103   }
104
105   public int getColumnIndex(int id) {
106     int col = Arrays.binarySearch(columns, new Integer JavaDoc(id), columnComparator);
107     return col;
108   }
109
110   public MOColumn getColumn(int index) {
111     return columns[index];
112   }
113
114   public int getColumnCount() {
115     return columns.length;
116   }
117
118   /**
119    * Creates a new row for this table with the supplied index and initial
120    * values. If the underlying table model is not a {@link MOMutableTableModel}
121    * instance or if one of the {@link MOTableRowListener} deny the row creation
122    * attempt then <code>null</code> will be returned.
123    * @param index
124    * the index OID of the new row.
125    * @param initialValues
126    * the initial values that should be assigned to the new row. If the array
127    * contains less values than this table has columns, default values will
128    * be created for the missing columns.
129    * @return
130    * the created <code>MOTableRow</code> instance or <code>null</code> if
131    * the row cannot be created.
132    */

133   public MOTableRow createRow(OID index, Variable[] initialValues) {
134     if (model instanceof MOMutableTableModel) {
135       Variable[] values = initialValues;
136       if (values.length < getColumnCount()) {
137         values = getDefaultValues();
138         System.arraycopy(initialValues, 0, values, 0, initialValues.length);
139       }
140       MOTableRow row =
141           ((MOMutableTableModel)model).createRow(index, values);
142       MOTableRowEvent rowEvent =
143           new MOTableRowEvent(this, this, row, MOTableRowEvent.CREATE, true);
144       fireRowChanged(rowEvent);
145       if (rowEvent.getVetoStatus() == SnmpConstants.SNMP_ERROR_SUCCESS) {
146         return row;
147       }
148     }
149     return null;
150   }
151
152   public MOTableRow createRow(OID index) {
153     return createRow(index, getDefaultValues());
154   }
155
156   /**
157    * Adds the supplied row to the underlying table model and fires the
158    * appropriate {@link MOTableRowEvent}. Since this method is typically
159    * called during the commit phase of a SET request that creates a table,
160    * it should be avoided to return an error here. Instead, error checking
161    * should be placed in the {@link #prepare} method.
162    * @param row
163    * the <code>MOTableRow</code> to add.
164    * @return
165    * <code>true</code> if the row has been added or <code>false</code>
166    * if it could not be added.
167    */

168   public boolean addRow(MOTableRow row) {
169     if (model instanceof MOMutableTableModel) {
170       MOTableRowEvent rowEvent =
171           new MOTableRowEvent(this, this, row, MOTableRowEvent.ADD, true);
172       fireRowChanged(rowEvent);
173       if (rowEvent.getVetoStatus() == SnmpConstants.SNMP_ERROR_SUCCESS) {
174         ((MOMutableTableModel)model).addRow(row);
175       }
176       return true;
177     }
178     return false;
179   }
180
181   public MOTableRow removeRow(OID index) {
182     if (model instanceof MOMutableTableModel) {
183       MOTableRow row = model.getRow(index);
184       if (row == null) {
185         return null;
186       }
187       MOTableRowEvent rowEvent =
188           new MOTableRowEvent(this, this, row, MOTableRowEvent.DELETE, true);
189       fireRowChanged(rowEvent);
190       if (rowEvent.getVetoStatus() == SnmpConstants.SNMP_ERROR_SUCCESS) {
191         return ((MOMutableTableModel)model).removeRow(index);
192       }
193     }
194     return null;
195   }
196
197   /**
198    * Removes all rows from this table. Before a row is removed the
199    * corresponding DELETE event is fired and listeners may veto these
200    * events for all rows. Only if there is no veto, a row will be deleted.
201    * The number of deleted rows is then returned.
202    * @return
203    * the number of removed rows or -1 if the table model does not support
204    * row removal.
205    */

206   public int removeAll() {
207     int count = 0;
208     if (model instanceof MOMutableTableModel) {
209       while (model.getRowCount() > 0) {
210         MOTableRow row = model.firstRow();
211         if (row != null) {
212           MOTableRowEvent rowEvent =
213               new MOTableRowEvent(this, this, row, MOTableRowEvent.DELETE, true);
214           fireRowChanged(rowEvent);
215           if (rowEvent.getVetoStatus() == SnmpConstants.SNMP_ERROR_SUCCESS) {
216             ((MOMutableTableModel) model).removeRow(row.getIndex());
217           }
218           count++;
219         }
220       }
221     }
222     else {
223       count = -1;
224     }
225     return count;
226   }
227
228   public void commit(SubRequest request) {
229     OID cellOID = request.getVariableBinding().getOid();
230     MOTableCellInfo cell = getCellInfo(cellOID);
231     MOMutableColumn col = (MOMutableColumn) getColumn(cell.getColumn());
232     if (logger.isDebugEnabled()) {
233       logger.debug("Committing sub-request ("+
234                    request.getVariableBinding()+") for column: "+col);
235     }
236     // Make sure changes are atomic -> sync whole table model
237
synchronized (model) {
238       MOMutableTableRow row;
239       if (hasNewRows(request.getRequest())) {
240         row = (MOMutableTableRow)
241             getNewRows(request.getRequest()).get(cell.getIndex());
242         addRow(row);
243       }
244       else {
245         row = (MOMutableTableRow) model.getRow(cell.getIndex());
246       }
247       Variable oldValue = null;
248       if (moChangeListeners != null) {
249         oldValue = row.getValue(cell.getColumn());
250         MOChangeEvent changeEvent =
251             new MOChangeEvent(this, new CellProxy(cell),
252                               cell.getCellOID(),
253                               oldValue,
254                               request.getVariableBinding().getVariable(),
255                               false);
256         fireBeforeMOChange(changeEvent);
257       }
258       ChangeSet changeSet = getPendingChangeSet(request, cell.getIndex());
259       // commit
260
col.commit(request, row, changeSet, cell.getColumn());
261       if (moChangeListeners != null) {
262         MOChangeEvent changeEvent =
263             new MOChangeEvent(this, new CellProxy(cell),
264                               cell.getCellOID(),
265                               oldValue,
266                               request.getVariableBinding().getVariable(),
267                               false);
268         fireAfterMOChange(changeEvent);
269       }
270       if (isChangeSetComplete(request, cell.getIndex(), cell.getColumn())) {
271         if (row instanceof MOMutableRow2PC) {
272           ((MOMutableRow2PC) row).commitRow(request, changeSet);
273         }
274         if (moTableRowListeners != null) {
275           MOTableRowEvent rowEvent =
276               new MOTableRowEvent(this, this, row, MOTableRowEvent.UPDATED);
277           fireRowChanged(rowEvent);
278         }
279       }
280     }
281   }
282
283   public final OID getIndexPart(OID anyOID) {
284     int offset = oid.size()+1;
285     if ((anyOID.size() <= offset) || (!anyOID.startsWith(oid))) {
286       return null;
287     }
288     return new OID(anyOID.getValue(), offset, anyOID.size() - offset);
289   }
290
291   public OID getCellOID(OID index, int col) {
292     OID retval = new OID(oid);
293     retval.append(columns[col].getColumnID());
294     retval.append(index);
295     return retval;
296   }
297
298   private MOTableCellInfo getNextCell(int col,
299                                       OID indexLowerBound,
300                                       boolean isLowerBoundIncluded) {
301     for (int i=col; i<columns.length; i++) {
302       Iterator it = model.tailIterator(indexLowerBound);
303       if (!it.hasNext()) {
304         if (indexLowerBound == null) {
305           return null;
306         }
307         indexLowerBound = null;
308         isLowerBoundIncluded = true;
309         continue;
310       }
311       else {
312         if ((indexLowerBound != null) && (!isLowerBoundIncluded)) {
313           MOTableRow row = (MOTableRow) it.next();
314           if (row.getIndex().compareTo(indexLowerBound) > 0) {
315             // the specified index does not exists so we can use this next one:
316
return new CellInfo(row.getIndex(), i, columns[i].getColumnID(),
317                                 row);
318           }
319         }
320         indexLowerBound = null;
321         isLowerBoundIncluded = true;
322         if (it.hasNext()) {
323           MOTableRow row = (MOTableRow) it.next();
324           if (row == null) {
325             continue;
326           }
327           return new CellInfo(row.getIndex(), i, columns[i].getColumnID(), row);
328         }
329       }
330     }
331     return null;
332   }
333
334   public OID find(MOScope range) {
335     MOTableCellInfo cellInfo = findCell(range, null);
336     if (cellInfo != null) {
337       return cellInfo.getCellOID();
338     }
339     return null;
340   }
341
342   protected MOTableCellInfo findCell(MOScope range, SubRequest request) {
343     synchronized (model) {
344       // determine column
345
if (model.getRowCount() == 0) {
346         return null;
347       }
348       MOTableCellInfo cellInfo = getCellInfo(range.getLowerBound());
349       int col = cellInfo.getColumn();
350       boolean exactMatch = true;
351       if (col < 0) {
352         col = (-col) - 1;
353         exactMatch = false;
354       }
355       if (col >= columns.length) {
356         return null;
357       }
358       boolean lowerIncluded = (!exactMatch) || range.isLowerIncluded();
359       RowCacheEntry rowEntry = null;
360       if (request != null) {
361         rowEntry = getWalkCacheEntry(request, cellInfo, lowerIncluded);
362       }
363       MOTableCellInfo next;
364       if (rowEntry != null) {
365         next = new CellInfo(rowEntry.row.getIndex(),
366                             col, cellInfo.getColumnID(), rowEntry.row);
367       }
368       else {
369         next = getNextCell(col, cellInfo.getIndex(), lowerIncluded);
370         if ((request != null) && (next != null) && (next.getColumn() == col)) {
371           addWalkCacheEntry(request, cellInfo.getIndex(), lowerIncluded,
372                             ((CellInfo)next).row);
373         }
374       }
375       if (next != null) {
376         OID cellOID = next.getCellOID();
377         if (range.isCovered(new OIDScope(cellOID))) {
378           return next;
379         }
380       }
381     }
382     return null;
383   }
384
385   private void addWalkCacheEntry(SubRequest request,
386                                  OID lowerBound,
387                                  boolean lowerIncluded,
388                                  MOTableRow row) {
389     if (walkCache == null) {
390       walkCache = new WeakHashMap(4);
391     }
392     walkCache.put(request.getRequest(),
393                   new RowCacheEntry(row, lowerBound, lowerIncluded));
394   }
395
396   private RowCacheEntry getWalkCacheEntry(SubRequest request,
397                                           MOTableCellInfo cellInfo,
398                                           boolean lowerIncluded) {
399     if (walkCache != null) {
400       RowCacheEntry entry = (RowCacheEntry) walkCache.get(request.getRequest());
401       if (entry == null) {
402         return null;
403       }
404       if (((entry.searchLowerBound == null) && (cellInfo.getIndex() == null)) ||
405           ((entry.searchLowerBound != null) &&
406            (entry.searchLowerBound.equals(cellInfo.getIndex())) &&
407            (lowerIncluded == entry.searchLowerBoundIncluded))) {
408         return entry;
409       }
410     }
411     return null;
412   }
413
414   public MOScope getScope() {
415     return this;
416   }
417
418   public Variable getValue(OID cellOID) {
419     MOTableCellInfo cell = getCellInfo(cellOID);
420     if ((cell.getIndex() != null) &&
421         (cell.getColumn() >= 0) && (cell.getColumn() < columns.length)) {
422       return getValue(cell.getIndex(), cell.getColumn());
423     }
424     return null;
425   }
426
427   public Variable getValue(OID index, int col) {
428     MOTableRow row = model.getRow(index);
429     return getValue(row, col);
430   }
431
432   protected Variable getValue(MOTableRow row, int col) {
433     if (row != null) {
434       return columns[col].getValue(row, col);
435     }
436     return null;
437   }
438
439   public void get(SubRequest request) {
440     OID cellOID = request.getVariableBinding().getOid();
441     MOTableCellInfo cell = getCellInfo(cellOID);
442     if ((cell.getIndex() != null) &&
443         (cell.getColumn() >= 0) && (cell.getColumn() < columns.length)) {
444       MOColumn col = getColumn(cell.getColumn());
445       MOTableRow row = model.getRow(cell.getIndex());
446       if (row == null) {
447         request.getVariableBinding().setVariable(Null.noSuchInstance);
448       }
449       else if (col != null) {
450         col.get(request, row, cell.getColumn());
451       }
452       else {
453         request.getStatus().setErrorStatus(PDU.noAccess);
454       }
455     }
456     else {
457       if (cell.getColumn() >= 0) {
458         request.getVariableBinding().setVariable(Null.noSuchInstance);
459       }
460       else {
461         request.getVariableBinding().setVariable(Null.noSuchObject);
462       }
463     }
464     request.completed();
465   }
466
467   public boolean next(SubRequest request) {
468     DefaultMOScope scope = new DefaultMOScope(request.getScope());
469     MOTableCellInfo nextCell;
470     while ((nextCell = findCell(scope, request)) != null) {
471       if (columns[nextCell.getColumn()].getAccess().isAccessibleForRead()) {
472         Variable value;
473         // Use row instance from cell info as shortcut if available
474
if ((nextCell instanceof CellInfo) &&
475             (((CellInfo)nextCell).getRow() != null)) {
476           value = getValue(((CellInfo)nextCell).getRow(), nextCell.getColumn());
477         }
478         else {
479           value = getValue(nextCell.getIndex(), nextCell.getColumn());
480         }
481         if (value == null) {
482           scope.setLowerBound(nextCell.getCellOID());
483           scope.setLowerIncluded(false);
484         }
485         else {
486           request.getVariableBinding().setOid(nextCell.getCellOID());
487           request.getVariableBinding().setVariable(value);
488           request.completed();
489           return true;
490         }
491       }
492       else {
493         if (nextCell.getColumn()+1 < getColumnCount()) {
494           OID nextColOID = new OID(getOID());
495           nextColOID.append(columns[nextCell.getColumn()+1].getColumnID());
496           scope.setLowerBound(nextColOID);
497           scope.setLowerIncluded(false);
498         }
499         else {
500           return false;
501         }
502       }
503     }
504     return false;
505   }
506
507   /**
508    * prepare
509    *
510    * @param request SubRequest
511    * @throws MOException
512    */

513   public void prepare(SubRequest request) {
514     OID cellOID = request.getVariableBinding().getOid();
515     MOTableCellInfo cell = getCellInfo(cellOID);
516     if (cell.getIndex() == null) {
517       request.getStatus().setErrorStatus(PDU.inconsistentName);
518       return;
519     }
520     if ((cell.getColumn() >= 0) && (cell.getColumn() < columns.length)) {
521       MOColumn col = getColumn(cell.getColumn());
522       if (logger.isDebugEnabled()) {
523         logger.debug("Preparing sub-request ("+request.getVariableBinding()+")"+
524                      " for column: "+col);
525       }
526       if ((col instanceof MOMutableColumn) &&
527           (col.getAccess().isAccessibleForWrite())) {
528         MOMutableColumn mcol = (MOMutableColumn)col;
529         // check index
530
if (getIndexDef().isValidIndex(cell.getIndex())) {
531           MOTableRow row = model.getRow(cell.getIndex());
532           if (row == null) {
533             // look for already prepared row
534
row = (MOTableRow)
535                 getNewRows(request.getRequest()).get(cell.getIndex());
536           }
537           if (row != null) {
538             prepare(request, cell, mcol, row, false);
539             request.completed();
540             return;
541           }
542           else if (model instanceof MOMutableTableModel) {
543             if (logger.isDebugEnabled()) {
544               logger.debug("Trying to create new row '"+cell.getIndex()+"'");
545             }
546             MOMutableTableModel mmodel = (MOMutableTableModel)model;
547             // create new row
548
try {
549               row = createRow(request, cell, mmodel);
550               if (row == null) {
551                 request.getStatus().setErrorStatus(PDU.noCreation);
552               }
553               else {
554                 prepare(request, cell, mcol, row, true);
555                 request.completed();
556               }
557             }
558             catch (UnsupportedOperationException JavaDoc ex) {
559               request.getStatus().setErrorStatus(PDU.noCreation);
560             }
561           }
562           else {
563             request.getStatus().setErrorStatus(PDU.noCreation);
564           }
565         }
566         else {
567           // invalid index
568
if (logger.isDebugEnabled()) {
569             logger.debug("Invalid index '"+cell.getIndex()+
570                          "' for row creation in table "+getID());
571           }
572           request.getStatus().setErrorStatus(PDU.noCreation);
573         }
574       }
575       else {
576         // read-only column
577
request.getStatus().setErrorStatus(PDU.notWritable);
578       }
579     }
580     else {
581       request.getStatus().setErrorStatus(PDU.noCreation);
582     }
583   }
584
585   private MOTableRow createRow(SubRequest request, MOTableCellInfo cell,
586                                MOMutableTableModel mmodel)
587       throws UnsupportedOperationException JavaDoc
588   {
589     MOColumn col = getColumn(cell.getColumn());
590     if (!col.getAccess().isAccessibleForCreate()) {
591       // creation not allowed
592
return null;
593     }
594     Variable[] initialValues = new Variable[getColumnCount()];
595     getChangesFromRequest(cell.getIndex(), null, request,
596                           initialValues, true, true);
597     MOTableRow row = mmodel.createRow(cell.getIndex(), initialValues);
598     getNewRows(request.getRequest()).put(row.getIndex(), row);
599     return row;
600   }
601
602   private void prepare(SubRequest request, MOTableCellInfo cell,
603                        MOMutableColumn mcol, MOTableRow row, boolean creation) {
604     if (moChangeListeners != null) {
605       MOChangeEvent changeEvent =
606           new MOChangeEvent(this, new CellProxy(cell),
607                             cell.getCellOID(),
608                             (creation) ? null : row.getValue(cell.getColumn()),
609                             request.getVariableBinding().getVariable(),
610                             true);
611       fireBeforePrepareMOChange(changeEvent);
612       if (changeEvent.getDenyReason() != PDU.noError) {
613         request.getStatus().setErrorStatus(changeEvent.getDenyReason());
614       }
615     }
616     ChangeSet changeSet = getPendingChangeSet(request, cell.getIndex());
617     if (changeSet == null) {
618       changeSet = addPendingChanges(request, row, creation);
619     }
620     if ((moTableRowListeners != null) && (!request.hasError())) {
621       if (isChangeSetComplete(request, row.getIndex(), cell.getColumn())) {
622         MOTableRowEvent rowEvent =
623             new MOTableRowEvent(this, this, row, changeSet, (creation) ?
624                                 MOTableRowEvent.CREATE :MOTableRowEvent.CHANGE,
625                                 true);
626         fireRowChanged(rowEvent);
627         if (rowEvent.getVetoStatus() != PDU.noError) {
628           if (rowEvent.getVetoColumn() >= 0) {
629             int colID = columns[rowEvent.getVetoColumn()].getColumnID();
630             OID prefix = new OID(getOID());
631             prefix.append(colID);
632             SubRequest r = request.getRequest().find(prefix);
633             if (r != null) {
634               r.getStatus().setErrorStatus(rowEvent.getVetoStatus());
635             }
636             else {
637               request.getRequest().setErrorStatus(rowEvent.getVetoStatus());
638             }
639           }
640           else {
641             request.getRequest().setErrorStatus(rowEvent.getVetoStatus());
642           }
643         }
644       }
645     }
646     if (request.getStatus().getErrorStatus() == PDU.noError) {
647       mcol.prepare(request, row, changeSet, cell.getColumn());
648       MOChangeEvent changeEvent =
649           new MOChangeEvent(this, new CellProxy(cell),
650                             cell.getCellOID(),
651                             row.getValue(cell.getColumn()),
652                             request.getVariableBinding().getVariable(),
653                             true);
654       fireAfterPrepareMOChange(changeEvent);
655       if (changeEvent.getDenyReason() != PDU.noError) {
656         request.getStatus().setErrorStatus(changeEvent.getDenyReason());
657       }
658       else {
659         if ((row instanceof MOMutableRow2PC) &&
660             (isChangeSetComplete(request, row.getIndex(), cell.getColumn()))) {
661           ((MOMutableRow2PC)row).prepareRow(request, changeSet);
662         }
663       }
664     }
665   }
666
667   protected int getChangesFromRequest(OID index,
668                                       MOTableRow row,
669                                       SubRequest request,
670                                       Variable[] values,
671                                       boolean setDefaultValues,
672                                       boolean newRow) {
673     int lastChangedColumn = -1;
674     // assign default values
675
if (setDefaultValues) {
676       for (int i = 0; (i < values.length) && (i < getColumnCount()); i++) {
677         if (columns[i] instanceof MOMutableColumn) {
678           values[i] = ((MOMutableColumn) columns[i]).getDefaultValue();
679         }
680       }
681     }
682     Request req = request.getRequest();
683     for (Iterator it = req.iterator(); it.hasNext();) {
684       SubRequest sreq = (SubRequest) it.next();
685       OID id = sreq.getVariableBinding().getOid();
686       MOTableCellInfo cellInfo = getCellInfo(id);
687       if (index.equals(cellInfo.getIndex())) {
688         int col = cellInfo.getColumn();
689         if ((col >= 0) && (col < values.length)) {
690           Variable v = sreq.getVariableBinding().getVariable();
691           // check that value is really changed
692
if ((v != null) &&
693               ((row == null) || (newRow) ||
694                (row.size() <= col) ||
695                (!v.equals(row.getValue(col))))) {
696             values[col] = v;
697             lastChangedColumn = col;
698           }
699         }
700       }
701     }
702     return lastChangedColumn;
703   }
704
705   protected boolean hasNewRows(Object JavaDoc key) {
706     return ((newRows != null) && (newRows.get(key) != null));
707   }
708
709   protected Map getNewRows(Object JavaDoc key) {
710     if (newRows == null) {
711       newRows = new WeakHashMap(4);
712     }
713     Map rowMap = (Map) newRows.get(key);
714     if (rowMap == null) {
715       rowMap = new HashMap(5);
716       newRows.put(key, rowMap);
717     }
718     return rowMap;
719   }
720
721   protected synchronized boolean isChangeSetComplete(SubRequest subRequest,
722                                                      OID index,
723                                                      int column) {
724     ChangeSet changeSet = getPendingChangeSet(subRequest, index);
725     if (changeSet != null) {
726       return (changeSet.getLastChangedColumn() == column);
727     }
728     return true;
729   }
730
731 /*
732   protected synchronized ChangeSet addPendingChange(SubRequest subRequest,
733                                                     OID index, int column,
734                                                     Variable value) {
735     if (pendingChanges == null) {
736       pendingChanges = new WeakHashMap(4);
737     }
738     Map rowMap = (Map) pendingChanges.get(subRequest.getRequest());
739     if (rowMap == null) {
740       rowMap = new HashMap(5);
741       pendingChanges.put(subRequest.getRequest(), rowMap);
742     }
743     ChangeSet changeSet = (ChangeSet) rowMap.get(index);
744     if (changeSet == null) {
745       changeSet = new ChangeSet(index, new Variable[getColumnCount()]);
746     }
747     changeSet.setValue(column, value);
748     return changeSet;
749   }
750 */

751
752   protected synchronized ChangeSet addPendingChanges(SubRequest subRequest,
753                                                      MOTableRow row,
754                                                      boolean newRow) {
755     if (pendingChanges == null) {
756       pendingChanges = new WeakHashMap(4);
757     }
758     Map rowMap = (Map) pendingChanges.get(subRequest.getRequest());
759     if (rowMap == null) {
760       rowMap = new HashMap(5);
761       pendingChanges.put(subRequest.getRequest(), rowMap);
762     }
763     Variable[] values = new Variable[getColumnCount()];
764     int lastChangedColumn =
765         getChangesFromRequest(row.getIndex(), row, subRequest,
766                               values, newRow, newRow);
767     ChangeSet changeSet = new ChangeSet(row.getIndex(), values);
768     changeSet.lastChangedColumn = lastChangedColumn;
769     rowMap.put(row.getIndex(), changeSet);
770     return changeSet;
771   }
772
773
774   protected ChangeSet getPendingChangeSet(SubRequest subRequest,
775                                           OID index) {
776     if (pendingChanges != null) {
777       Map rowMap = (Map) pendingChanges.get(subRequest.getRequest());
778       if (rowMap != null) {
779         return (ChangeSet) rowMap.get(index);
780       }
781     }
782     return null;
783   }
784
785
786   public void cleanup(SubRequest request) {
787     OID cellOID = request.getVariableBinding().getOid();
788     MOTableCellInfo cell = getCellInfo(cellOID);
789     if (cell.getIndex() == null) {
790       return;
791     }
792     MOColumn col = getColumn(cell.getColumn());
793     if (logger.isDebugEnabled()) {
794       logger.debug("Cleaning-up sub-request ("+
795                    request.getVariableBinding()+") for column: "+col);
796     }
797     MOMutableTableRow row = (MOMutableTableRow) model.getRow(cell.getIndex());
798     if ((row != null) && (col instanceof MOMutableColumn)) {
799       ((MOMutableColumn) col).cleanup(request, row, cell.getColumn());
800     }
801     if ((row instanceof MOMutableRow2PC) &&
802         isChangeSetComplete(request, row.getIndex(), cell.getColumn())) {
803       ((MOMutableRow2PC)row).cleanupRow(request,
804                                         getPendingChangeSet(request,
805           row.getIndex()));
806     }
807     request.completed();
808   }
809
810   public void undo(SubRequest request) {
811     OID cellOID = request.getVariableBinding().getOid();
812     MOTableCellInfo cell = getCellInfo(cellOID);
813     MOMutableColumn col = (MOMutableColumn) getColumn(cell.getColumn());
814     if (logger.isDebugEnabled()) {
815       logger.debug("Undoing sub-request ("+
816                    request.getVariableBinding()+") for column: "+col);
817     }
818     if (hasNewRows(request.getRequest())) {
819       ((MOMutableTableModel)model).removeRow(cell.getIndex());
820     }
821     else {
822       MOMutableTableRow row = (MOMutableTableRow) model.getRow(cell.getIndex());
823       if (row != null) {
824         col.undo(request, row, cell.getColumn());
825       }
826       if ((row instanceof MOMutableRow2PC) &&
827           isChangeSetComplete(request, row.getIndex(), cell.getColumn())) {
828         ((MOMutableRow2PC)row).undoRow(request, getPendingChangeSet(request, row.getIndex()));
829       }
830     }
831   }
832
833   public OID getOID() {
834     return oid;
835   }
836
837   public void setModel(MOTableModel model) {
838     this.model = model;
839   }
840
841   public void setVolatile(boolean isVolatile) {
842     this.isVolatile = isVolatile;
843   }
844
845   public MOTableModel getModel() {
846     return model;
847   }
848
849   public MOColumn[] getColumns() {
850     return columns;
851   }
852
853   public MOTableIndex getIndexDef() {
854     return indexDef;
855   }
856
857   public boolean isVolatile() {
858     return isVolatile;
859   }
860
861   public OID getLowerBound() {
862     return oid;
863   }
864
865   public OID getUpperBound() {
866     OID upperBound = new OID(oid);
867     int lastID = oid.size()-1;
868     /**
869      * This is not quite correct because we would have to search up the tree
870      * if the last sub ID is 0xFFFFFFFF, but since a table OID must end on 1
871      * by SMI rules we should be on the safe side here.
872      */

873     upperBound.set(lastID, oid.get(lastID)+1);
874     return upperBound;
875   }
876
877   public boolean isLowerIncluded() {
878     return false;
879   }
880
881   public boolean isUpperIncluded() {
882     return false;
883   }
884
885   public boolean isCovered(MOScope other) {
886     return DefaultMOScope.covers(this, other);
887   }
888
889   public boolean isOverlapping(MOScope other) {
890     return DefaultMOScope.overlaps(this, other);
891   }
892
893   public synchronized void addMOChangeListener(MOChangeListener l) {
894     if (moChangeListeners == null) {
895       moChangeListeners = new Vector(2);
896     }
897     moChangeListeners.add(l);
898   }
899
900   public synchronized void removeMOChangeListener(MOChangeListener l) {
901     if (moChangeListeners != null) {
902       moChangeListeners.remove(l);
903     }
904   }
905
906   protected void fireBeforePrepareMOChange(MOChangeEvent changeEvent) {
907     if (moChangeListeners != null) {
908       Vector listeners = moChangeListeners;
909       int count = listeners.size();
910       for (int i = 0; i < count; i++) {
911         ((MOChangeListener) listeners.elementAt(i)).beforePrepareMOChange(changeEvent);
912       }
913     }
914   }
915
916   protected void fireAfterPrepareMOChange(MOChangeEvent changeEvent) {
917     if (moChangeListeners != null) {
918       Vector listeners = moChangeListeners;
919       int count = listeners.size();
920       for (int i = 0; i < count; i++) {
921         ((MOChangeListener) listeners.elementAt(i)).afterPrepareMOChange(changeEvent);
922       }
923     }
924   }
925
926   protected void fireBeforeMOChange(MOChangeEvent changeEvent) {
927     if (moChangeListeners != null) {
928       Vector listeners = moChangeListeners;
929       int count = listeners.size();
930       for (int i = 0; i < count; i++) {
931         ((MOChangeListener) listeners.elementAt(i)).beforeMOChange(changeEvent);
932       }
933     }
934   }
935
936   protected void fireAfterMOChange(MOChangeEvent changeEvent) {
937     if (moChangeListeners != null) {
938       Vector listeners = moChangeListeners;
939       int count = listeners.size();
940       for (int i = 0; i < count; i++) {
941         ((MOChangeListener) listeners.elementAt(i)).afterMOChange(changeEvent);
942       }
943     }
944   }
945
946   public synchronized void addMOTableRowListener(MOTableRowListener l) {
947     if (moTableRowListeners == null) {
948       moTableRowListeners = new Vector(2);
949     }
950     moTableRowListeners.add(l);
951   }
952
953   public synchronized void removeMOTableRowListener(MOTableRowListener l) {
954     if (moTableRowListeners != null) {
955       moTableRowListeners.remove(l);
956     }
957   }
958
959   protected void fireRowChanged(MOTableRowEvent event) {
960     if (moTableRowListeners != null) {
961       Vector listeners = moTableRowListeners;
962       int count = listeners.size();
963       for (int i = 0; i < count; i++) {
964         ((MOTableRowListener) listeners.elementAt(i)).rowChanged(event);
965       }
966     }
967   }
968
969   public static class ChangeSet implements MOTableRow {
970
971     private OID index;
972     private Variable[] values;
973     private int lastChangedColumn = -1;
974
975     public ChangeSet(OID index, Variable[] values) {
976       this.index = index;
977       this.values = values;
978     }
979
980     public OID getIndex() {
981       return index;
982     }
983
984     public int getLastChangedColumn() {
985       return lastChangedColumn;
986     }
987
988     public void setValue(int column, Variable value) {
989       values[column] = value;
990       this.lastChangedColumn = column;
991     }
992
993     public Variable getValue(int column) {
994       return values[column];
995     }
996
997     public MOTableRow getBaseRow() {
998       return null;
999     }
1000
1001    public int size() {
1002      return values.length;
1003    }
1004
1005    public void setBaseRow(MOTableRow baseRow) {
1006      throw new UnsupportedOperationException JavaDoc();
1007    }
1008  }
1009
1010  class CellProxy implements ManagedObject {
1011
1012    private MOTableCellInfo cellInfo;
1013    private MOScope scope;
1014
1015    public CellProxy(MOTableCellInfo cellInfo) {
1016      this.cellInfo = cellInfo;
1017      this.scope = new OIDScope(cellInfo.getCellOID());
1018    }
1019
1020    public MOScope getScope() {
1021      return scope;
1022    }
1023
1024    public OID find(MOScope range) {
1025      if (range.isCovered(scope)) {
1026        return cellInfo.getCellOID();
1027      }
1028      return null;
1029    }
1030
1031    public void get(SubRequest request) {
1032      DefaultMOTable.this.get(request);
1033    }
1034
1035    public boolean next(SubRequest request) {
1036      return DefaultMOTable.this.next(request);
1037    }
1038
1039    public void prepare(SubRequest request) {
1040      DefaultMOTable.this.prepare(request);
1041    }
1042
1043    public void commit(SubRequest request) {
1044      DefaultMOTable.this.commit(request);
1045    }
1046
1047    public void undo(SubRequest request) {
1048      DefaultMOTable.this.undo(request);
1049    }
1050
1051    public void cleanup(SubRequest request) {
1052      DefaultMOTable.this.cleanup(request);
1053    }
1054
1055    public MOTable getTable() {
1056      return DefaultMOTable.this;
1057    }
1058  }
1059
1060  class CellInfo implements MOTableCellInfo {
1061
1062    private OID index;
1063    private int id = 0;
1064    private int col = -1;
1065    private MOTableRow row;
1066
1067    public CellInfo(OID oid) {
1068      this.index = getIndexPart(oid);
1069      if ((oid.size() > DefaultMOTable.this.oid.size()) &&
1070          (oid.startsWith(DefaultMOTable.this.oid))) {
1071        id = oid.get(DefaultMOTable.this.oid.size());
1072      }
1073/*
1074      else {
1075        id = columns[0].getColumnID();
1076      }
1077*/

1078    }
1079
1080    public CellInfo(OID index, int column, int columnID) {
1081      this.index = index;
1082      this.col = column;
1083      this.id = columnID;
1084    }
1085
1086    public CellInfo(OID index, int column, int columnID, MOTableRow row) {
1087      this(index, column, columnID);
1088      this.row = row;
1089    }
1090
1091    public OID getIndex() {
1092      return index;
1093    }
1094
1095    public int getColumn() {
1096      if (col < 0) {
1097        col = getColumnIndex(id);
1098      }
1099      return col;
1100    }
1101
1102    public int getColumnID() {
1103      return id;
1104    }
1105
1106    public OID getCellOID() {
1107      return DefaultMOTable.this.getCellOID(index, col);
1108    }
1109
1110    public MOTableRow getRow() {
1111      return row;
1112    }
1113  }
1114
1115  public OID getID() {
1116    return getLowerBound();
1117  }
1118
1119  public void load(MOInput input) throws IOException {
1120    if (input.getImportMode() == ImportModes.REPLACE_CREATE) {
1121      int count = removeAll();
1122      if (logger.isDebugEnabled()) {
1123        logger.debug("Removed "+count+" rows from "+getID()+
1124                     " because importing with a REPLACE import mode");
1125      }
1126    }
1127    Sequence seq = input.readSequence();
1128    for (int i=0; i<seq.getSize(); i++) {
1129      IndexedVariables rowValues = input.readIndexedVariables();
1130      boolean rowExists = model.containsRow(rowValues.getIndex());
1131      if ((input.getImportMode() == ImportModes.CREATE) && (rowExists)) {
1132        logger.debug("Row '" + rowValues.getIndex() +
1133                     "' not imported because it already exists in table " +
1134                     getID() + " and import mode is CREATE");
1135        continue;
1136      }
1137      if (rowExists) {
1138        removeRow(rowValues.getIndex());
1139      }
1140      /**@todo Do real update here instead of delete/create */
1141      if ((rowExists) ||
1142          ((input.getImportMode() == ImportModes.CREATE) ||
1143           (input.getImportMode() == ImportModes.REPLACE_CREATE) ||
1144           (input.getImportMode() == ImportModes.UPDATE_CREATE))) {
1145        MOTableRow row = null;
1146        try {
1147          row = createRow(rowValues.getIndex(), rowValues.getValues());
1148        }
1149        catch (UnsupportedOperationException JavaDoc uoex) {
1150          logger.debug("Could not create row by row factory: " +
1151                       uoex.getMessage());
1152          // ignore
1153
}
1154        if (row == null) {
1155          row =
1156              new DefaultMOTableRow(rowValues.getIndex(), rowValues.getValues());
1157          fireRowChanged(new MOTableRowEvent(this,
1158                                             this, row, MOTableRowEvent.CREATE));
1159        }
1160        addRow(row);
1161      }
1162    }
1163  }
1164
1165  public void save(MOOutput output) throws IOException {
1166    List rowsToSave = new LinkedList();
1167    synchronized (model) {
1168      for (Iterator it = model.iterator(); it.hasNext(); ) {
1169        MOTableRow row = (MOTableRow) it.next();
1170        boolean volatileRow = false;
1171        for (int i = 0; i < columns.length; i++) {
1172          if (columns[i].isVolatile(row, i)) {
1173            volatileRow = true;
1174            break;
1175          }
1176        }
1177        if (!volatileRow) {
1178          Variable[] values = new Variable[columns.length];
1179          for (int i=0; i<columns.length; i++) {
1180            values[i] = row.getValue(i);
1181          }
1182          IndexedVariables rowValues =
1183              new IndexedVariables(row.getIndex(), values);
1184          rowsToSave.add(rowValues);
1185        }
1186      }
1187    }
1188    Sequence group = new Sequence(rowsToSave.size());
1189    output.writeSequence(group);
1190    for (Iterator it = rowsToSave.iterator(); it.hasNext(); ) {
1191      IndexedVariables rowValues = (IndexedVariables) it.next();
1192      output.writeIndexedVariables(rowValues);
1193    }
1194  }
1195
1196  public Variable[] getDefaultValues() {
1197    Variable[] values = new Variable[getColumnCount()];
1198    for (int i = 0; (i < values.length); i++) {
1199      if (columns[i] instanceof MOMutableColumn) {
1200        values[i] = ((MOMutableColumn) columns[i]).getDefaultValue();
1201      }
1202    }
1203    return values;
1204  }
1205
1206  public String JavaDoc toString() {
1207    return "DefaultMOTable[id="+getID()+",index="+getIndexDef()+",columns="+
1208        Arrays.asList(getColumns())+"]";
1209  }
1210
1211  public boolean covers(OID oid) {
1212    return isCovered(new DefaultMOScope(oid, true, oid, true));
1213  }
1214
1215
1216  private static class RowCacheEntry {
1217    private MOTableRow row;
1218    private OID searchLowerBound;
1219    private boolean searchLowerBoundIncluded;
1220
1221    RowCacheEntry(MOTableRow row,
1222                  OID searchLowerBound, boolean searchLowerBoundIncluded) {
1223      this.row = row;
1224      this.searchLowerBound = searchLowerBound;
1225      this.searchLowerBoundIncluded = searchLowerBoundIncluded;
1226    }
1227  }
1228}
1229
Popular Tags