001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.dbschema.nodes;
043:
044: import java.beans.PropertyChangeListener;
045: import java.beans.PropertyChangeEvent;
046: import java.util.*;
047:
048: import org.openide.nodes.Children;
049: import org.openide.nodes.Node;
050: import org.openide.util.WeakListeners;
051:
052: import org.netbeans.modules.dbschema.*;
053:
054: /** Normal implementation of children list for a table element node.
055: */
056: public class TableChildren extends Children.Keys {
057: /** Converts property names to filter. */
058: protected static HashMap propToFilter;
059:
060: /** The table element whose subelements are represented. */
061: protected DBElement element;
062: /** Filter for elements, or <code>null</code> to disable. */
063: protected TableElementFilter filter;
064: /** Factory for creating new child nodes. */
065: protected DBElementNodeFactory factory;
066: /** Weak listener to the element and filter changes */
067: private PropertyChangeListener wPropL;
068: /** Listener to the element and filter changes. This reference must
069: * be kept to prevent the listener from finalizing when we are alive */
070: private DBElementListener propL;
071: /** Central memory of mankind is used when some elements are changed */
072: protected Collection[] cpl;
073: /** Flag saying whether we have our nodes initialized */
074: private boolean nodesInited = false;
075:
076: /** For sorting groups of elements. */
077: private static Comparator comparator = new Comparator() {
078: public int compare(Object o1, Object o2) {
079: if (o1 instanceof DBMemberElement)
080: if (o2 instanceof DBMemberElement)
081: return ((DBMemberElement) o1).getName().getName()
082: .compareToIgnoreCase(
083: ((DBMemberElement) o2).getName()
084: .getName());
085: else
086: return -1;
087: else if (o2 instanceof DBMemberElement)
088: return 1;
089: else
090: return 0;
091: }
092: };
093:
094: static {
095: propToFilter = new HashMap();
096: propToFilter.put(DBElementProperties.PROP_TABLES, new Integer(
097: TableElementFilter.TABLE | TableElementFilter.VIEW));
098: propToFilter.put(DBElementProperties.PROP_COLUMNS, new Integer(
099: TableElementFilter.COLUMN));
100: propToFilter.put(DBElementProperties.PROP_COLUMN_PAIRS,
101: new Integer(TableElementFilter.COLUMN_PAIR));
102: propToFilter.put(DBElementProperties.PROP_INDEXES, new Integer(
103: TableElementFilter.INDEX));
104: propToFilter.put(DBElementProperties.PROP_KEYS, new Integer(
105: TableElementFilter.FK));
106: }
107:
108: /** Create class children with the default factory.
109: * The children are initially unfiltered.
110: * @param element attached class element (non-<code>null</code>)
111: */
112: public TableChildren(final DBElement element) {
113: this (DefaultDBFactory.READ_ONLY, element);
114: }
115:
116: /** Create class children.
117: * The children are initially unfiltered.
118: * @param factory the factory to use to create new children
119: * @param element attached class element (non-<code>null</code>)
120: */
121: public TableChildren(final DBElementNodeFactory factory,
122: final DBElement element) {
123: super ();
124: this .element = element;
125: this .factory = factory;
126: this .filter = null;
127: }
128:
129: /********** Implementation of filter cookie **********/
130:
131: /* @return The class of currently asociated filter or null
132: * if no filter is asociated with these children.
133: */
134: public Class getFilterClass() {
135: return TableElementFilter.class;
136: }
137:
138: /* @return The filter currently asociated with these children
139: */
140: public Object getFilter() {
141: return filter;
142: }
143:
144: /* Sets new filter for these children.
145: * @param filter New filter. Null == disable filtering.
146: */
147: public void setFilter(final Object filter) {
148: if (!(filter instanceof TableElementFilter))
149: throw new IllegalArgumentException();
150:
151: this .filter = (TableElementFilter) filter;
152: // change element nodes according to the new filter
153: if (nodesInited)
154: refreshAllKeys();
155: }
156:
157: // Children implementation ..............................................................
158:
159: /* Overrides initNodes to run the preparation task of the
160: * source element, call refreshKeys and start to
161: * listen to the changes in the element too. */
162: protected void addNotify() {
163: refreshAllKeys();
164: // listen to the changes in the class element
165: if (wPropL == null) {
166: propL = new DBElementListener();
167: wPropL = WeakListeners.propertyChange(propL, element);
168: }
169:
170: element.addPropertyChangeListener(wPropL);
171: nodesInited = true;
172: }
173:
174: protected void removeNotify() {
175: setKeys(java.util.Collections.EMPTY_SET);
176: nodesInited = false;
177: }
178:
179: /* Creates node for given key.
180: * The node is created using node factory.
181: */
182: protected Node[] createNodes(final Object key) {
183: if (key instanceof ColumnElement)
184: return new Node[] { factory
185: .createColumnNode((ColumnElement) key) };
186:
187: if (key instanceof ColumnPairElement)
188: return new Node[] { factory
189: .createColumnPairNode((ColumnPairElement) key) };
190:
191: if (element instanceof TableElement) {
192: boolean viewSupport = isViewSupport((TableElement) element);
193: if (((TableElement) element).isTableOrView() || viewSupport) {
194: // if (((TableElement) element).isTableOrView()) {
195: if (key instanceof IndexElement)
196: return new Node[] { factory
197: .createIndexNode((IndexElement) key) };
198: if (key instanceof ForeignKeyElement)
199: return new Node[] { factory
200: .createForeignKeyNode((ForeignKeyElement) key) };
201: }
202: }
203:
204: // ?? unknown type
205: return new Node[0];
206: }
207:
208: /************** utility methods ************/
209:
210: /** Updates all the keys (elements) according to the current filter &
211: * ordering.
212: */
213: protected void refreshAllKeys() {
214: cpl = new Collection[getOrder().length];
215: refreshKeys(TableElementFilter.ALL);
216: }
217:
218: /** Updates all the keys with given filter.
219: */
220: protected void refreshKeys(int filter) {
221: int[] order = getOrder();
222: LinkedList keys = new LinkedList();
223:
224: // build ordered and filtered keys for the subelements
225: for (int i = 0; i < order.length; i++)
226: if (((order[i] & filter) != 0) || (cpl[i] == null))
227: keys.addAll(cpl[i] = getKeysOfType(order[i]));
228: else
229: keys.addAll(cpl[i]);
230:
231: // set new keys
232: setKeys(keys);
233: }
234:
235: /** Filters and returns the keys of specified type.
236: */
237: protected Collection getKeysOfType(final int elementType) {
238: LinkedList keys = new LinkedList();
239:
240: ColumnElement[] columns = null;
241: ColumnPairElement[] pairs = null;
242: if (element instanceof TableElement)
243: columns = ((TableElement) element).getColumns();
244: if (element instanceof IndexElement)
245: columns = ((IndexElement) element).getColumns();
246: if (element instanceof ForeignKeyElement)
247: pairs = ((ForeignKeyElement) element).getColumnPairs();
248:
249: if ((elementType & TableElementFilter.COLUMN) != 0)
250: if (columns != null)
251: filterModifiers(columns, keys);
252: else if (pairs != null)
253: filterModifiers(pairs, keys);
254:
255: if (element instanceof TableElement) {
256:
257: boolean viewSupport = isViewSupport(((TableElement) element));
258:
259: if (((TableElement) element).isTableOrView() || viewSupport) {
260: // if (((TableElement) element).isTableOrView()) {
261: if ((elementType & TableElementFilter.INDEX) != 0)
262: filterModifiers(((TableElement) element)
263: .getIndexes(), keys);
264: if ((elementType & TableElementFilter.FK) != 0)
265: filterModifiers(((TableElement) element).getKeys(),
266: keys);
267: }
268: }
269:
270: if ((filter == null) || filter.isSorted())
271: Collections.sort(keys, comparator);
272:
273: return keys;
274: }
275:
276: /* check is view are suppported for this table
277: * database name could be null see bug 53887
278: **/
279: private boolean isViewSupport(TableElement element) {
280: String db = element.getDeclaringSchema()
281: .getDatabaseProductName();
282: boolean viewSupport = false;
283: if (db != null) {
284: db = db.toLowerCase();
285: viewSupport = (db.indexOf("oracle") != -1 || db
286: .indexOf("microsoft sql server") != -1) ? true
287: : false;
288: }
289: return viewSupport;
290: }
291:
292: /** Filters MemberElements for modifiers, and adds them to the given collection.
293: */
294: private void filterModifiers(DBMemberElement[] elements,
295: Collection keys) {
296: int i, k = elements.length;
297: for (i = 0; i < k; i++)
298: keys.add(elements[i]);
299: }
300:
301: /** Returns order form filter.
302: */
303: protected int[] getOrder() {
304: return (filter == null || (filter.getOrder() == null)) ? TableElementFilter.DEFAULT_ORDER
305: : filter.getOrder();
306: }
307:
308: // innerclasses ...........................................................................
309:
310: /** The listener for listening to the property changes in the filter.
311: */
312: private final class DBElementListener implements
313: PropertyChangeListener {
314: public DBElementListener() {
315: }
316:
317: /** This method is called when the change of properties occurs in the element.
318: * PENDING - (for Hanz - should be implemented better, change only the
319: * keys which belong to the changed property).
320: * -> YES MY LORD! ANOTHER WISH?
321: */
322: public void propertyChange(PropertyChangeEvent evt) {
323: Integer i = (Integer) propToFilter.get(evt
324: .getPropertyName());
325: if (i != null)
326: refreshKeys(i.intValue());
327: }
328: } // end of ElementListener inner class*/
329: }
|