001: package prefuse.data.column;
002:
003: import java.lang.reflect.Method;
004: import java.util.Arrays;
005: import java.util.logging.Logger;
006:
007: import prefuse.data.DataReadOnlyException;
008: import prefuse.data.DataTypeException;
009:
010: /**
011: * Column implementation for storing arbitrary Object values.
012: *
013: * @author <a href="http://jheer.org">jeffrey heer</a>
014: */
015: public class ObjectColumn extends AbstractColumn {
016:
017: private Object[] m_values;
018: private int m_size;
019:
020: /**
021: * Create a new empty ObjectColumn. The type is assumed to be Object.
022: */
023: public ObjectColumn() {
024: this (Object.class);
025: }
026:
027: /**
028: * Create a new ObjectColumn.
029: * @param type the data type of Objects in this column
030: */
031: public ObjectColumn(Class type) {
032: this (type, 0, 10, null);
033: }
034:
035: /**
036: * Create a new ObjectColumn. The type is assumed to be Object.
037: * @param nrows the initial size of the column
038: */
039: public ObjectColumn(int nrows) {
040: this (Object.class, nrows, nrows, null);
041: }
042:
043: /**
044: * Create a new ObjectColumn.
045: * @param type the data type of Objects in this column
046: * @param nrows the initial size of the column
047: */
048: public ObjectColumn(Class type, int nrows) {
049: this (type, nrows, nrows, null);
050: }
051:
052: /**
053: * Create a new ObjectColumn.
054: * @param type the data type of Objects in this column
055: * @param nrows the initial size of the column
056: * @param capacity the initial capacity of the column
057: * @param defaultValue the default value for the column. If this value
058: * is cloneable, it will be cloned when assigned as defaultValue, otherwise
059: * the input reference will be used for every default value.
060: */
061: public ObjectColumn(Class type, int nrows, int capacity,
062: Object defaultValue) {
063: super (type, defaultValue);
064: if (capacity < nrows) {
065: throw new IllegalArgumentException(
066: "Capacity value can not be less than the row count.");
067: }
068: m_values = new Object[capacity];
069: try {
070: // since Object's clone method is protected, we default to
071: // using reflection to create clones.
072: Cloneable def = (Cloneable) defaultValue;
073: Method m = def.getClass()
074: .getMethod("clone", (Class[]) null);
075: for (int i = 0; i < capacity; ++i) {
076: m_values[i] = m.invoke(m_defaultValue, (Object[]) null);
077: }
078: } catch (Exception e) {
079: if (defaultValue != null) {
080: Logger
081: .getLogger(getClass().getName())
082: .fine(
083: "Default value of type \""
084: + defaultValue.getClass()
085: .getName()
086: + "\" is not "
087: + "cloneable. Using Object reference directly.");
088: }
089: Arrays.fill(m_values, defaultValue);
090: }
091: m_size = nrows;
092: }
093:
094: // ------------------------------------------------------------------------
095: // Column Metadata
096:
097: /**
098: * @see prefuse.data.column.Column#getRowCount()
099: */
100: public int getRowCount() {
101: return m_size;
102: }
103:
104: /**
105: * @see prefuse.data.column.Column#setMaximumRow(int)
106: */
107: public void setMaximumRow(int nrows) {
108: if (nrows > m_values.length) {
109: int capacity = Math.max((3 * m_values.length) / 2 + 1,
110: nrows);
111: Object[] values = new Object[capacity];
112: System.arraycopy(m_values, 0, values, 0, m_size);
113: try {
114: // since Object's clone method is protected, we default to
115: // using reflection to create clones.
116: Cloneable def = (Cloneable) m_defaultValue;
117: Method m = def.getClass().getMethod("clone",
118: (Class[]) null);
119: for (int i = m_size; i < capacity; ++i) {
120: values[i] = m.invoke(m_defaultValue,
121: (Object[]) null);
122: }
123: } catch (Exception e) {
124: Arrays.fill(values, m_size, capacity, m_defaultValue);
125: }
126: m_values = values;
127: }
128: m_size = nrows;
129: }
130:
131: // ------------------------------------------------------------------------
132: // Data Access Methods
133:
134: public void revertToDefault(int row) {
135: try {
136: // since Object's clone method is protected, we default to
137: // using reflection to create clones.
138: Cloneable def = (Cloneable) m_defaultValue;
139: Method m = def.getClass()
140: .getMethod("clone", (Class[]) null);
141: set(m.invoke(m_defaultValue, (Object[]) null), row);
142: } catch (Exception e) {
143: set(m_defaultValue, row);
144: }
145: }
146:
147: /**
148: * Get the data value at the specified row
149: * @param row the row from which to retrieve the value
150: * @return the data value
151: */
152: public Object get(int row) {
153: if (row < 0 || row > m_size) {
154: throw new IllegalArgumentException(
155: "Row index out of bounds: " + row);
156: }
157: return m_values[row];
158: }
159:
160: /**
161: * Set the data value at the specified row
162: * @param val the value to set
163: * @param row the row at which to set the value
164: */
165: public void set(Object val, int row) {
166: if (m_readOnly) {
167: throw new DataReadOnlyException();
168: } else if (row < 0 || row > m_size) {
169: throw new IllegalArgumentException(
170: "Row index out of bounds: " + row);
171: } else if (val == null || canSet(val.getClass())) {
172: // get the previous value
173: Object prev = m_values[row];
174:
175: // exit early if no change
176: // do we trust .equals() here? for now, no.
177: if (prev == val)
178: return;
179:
180: // set the new value
181: m_values[row] = val;
182:
183: // fire a change event
184: fireColumnEvent(row, prev);
185: } else {
186: throw new DataTypeException(val.getClass());
187: }
188: }
189:
190: } // end of class ObjectColumn
|