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.Collection;
047: import java.util.Collections;
048: import java.util.LinkedList;
049: import java.util.logging.Level;
050: import java.util.logging.Logger;
051:
052: import org.openide.nodes.Children;
053: import org.openide.nodes.Node;
054: import org.openide.util.RequestProcessor;
055: import org.openide.util.WeakListeners;
056:
057: import org.netbeans.modules.dbschema.*;
058: import org.netbeans.modules.dbschema.jdbcimpl.*;
059:
060: /** Normal implementation of children for source element nodes.
061: * <P>
062: * Ordering and filtering of the children can be customized
063: * using {@link SourceElementFilter}.
064: * {@link FilterCookie} is implemented to provide a means
065: * for user customization of the filter.
066: * <p>The child list listens to changes in the source element, as well as the filter, and
067: * automatically updates itself as appropriate.
068: * <p>A child factory can be used to cause the children list to create
069: * non-{@link DefaultFactory default} child nodes, if desired, both at the time of the creation
070: * of the children list, and when new children are added.
071: * <p>The children list may be unattached to any source element temporarily,
072: * in which case it will have no children (except possibly an error indicator).
073: *
074: * @author Dafe Simonek, Jan Jancura
075: */
076: public class SchemaRootChildren extends Children.Keys {
077:
078: /** The key describing state of source element */
079: static final Object NOT_KEY = new Object();
080: /** The key describing state of source element */
081: static final Object ERROR_KEY = new Object();
082:
083: /** The element whose subelements are represented. */
084: protected SchemaElement element;
085: /** Factory for obtaining class nodes. */
086: protected DBElementNodeFactory factory;
087: /** Weak listener to the element and filter changes */
088: private PropertyChangeListener wPropL;
089: /** Listener to the element and filter changes. This reference must
090: * be kept to prevent the listener from finalizing when we are alive */
091: private DBElementListener propL;
092: /** Flag saying whether we have our nodes initialized */
093: private boolean nodesInited = false;
094:
095: private boolean parseStatus = false;
096: private Object parseLock = new Object();
097:
098: private org.netbeans.modules.dbschema.jdbcimpl.DBschemaDataObject obj;
099:
100: /** Create a children list.
101: * @param factory a factory for creating children
102: * @param obj database schema data object
103: */
104: public SchemaRootChildren(
105: final DBElementNodeFactory factory,
106: org.netbeans.modules.dbschema.jdbcimpl.DBschemaDataObject obj) {
107: super ();
108: this .factory = factory;
109: this .obj = obj;
110: }
111:
112: // Children implementation ..............................................................
113:
114: /* Overrides initNodes to run the preparation task of the
115: * source element, call refreshKeys and start to
116: * listen to the changes in the element too. */
117: protected void addNotify() {
118: SchemaElement el = getElement();
119:
120: if (el != null) {
121: // listen to the source element property changes
122: if (wPropL == null) {
123: propL = new DBElementListener();
124: wPropL = WeakListeners.propertyChange(propL, el);
125: }
126: el.addPropertyChangeListener(wPropL);
127: }
128:
129: refreshKeys();
130: }
131:
132: protected void removeNotify() {
133: setKeys(java.util.Collections.EMPTY_SET);
134: nodesInited = false;
135: }
136:
137: /* Create nodes for given key.
138: * The node is created using node factory.
139: */
140: protected Node[] createNodes(final Object key) {
141: if (key instanceof SchemaElement)
142: return new Node[] { factory
143: .createSchemaNode((SchemaElement) key) };
144: if (NOT_KEY.equals(key))
145: return new Node[] { factory.createWaitNode() };
146:
147: // never should get here
148: Logger.getLogger("global").log(
149: Level.INFO,
150: null,
151: new Exception(
152: "DbSchema: Error node created for object "
153: + key
154: + " (class "
155: + ((key == null) ? "null" : key
156: .getClass().getName()) + ")"));
157: return new Node[] { factory.createErrorNode() };
158: }
159:
160: // main public methods ..................................................................
161:
162: /** Get the currently attached source element.
163: * @return the element, or <code>null</code> if unattached
164: */
165: public SchemaElement getElement() {
166: if (element == null && !parseStatus) {
167: refreshKeys2();
168:
169: RequestProcessor.getDefault().post(new Runnable() {
170: public void run() {
171: synchronized (parseLock) {
172: if (!parseStatus) {
173: nodesInited = true;
174: setElement(obj.getSchema());
175: parseStatus = true;
176: }
177: }
178: }
179: }, 0);
180: }
181:
182: return element;
183: }
184:
185: /** Set a new source element to get information about children from.
186: * @param element the new element, or <code>null</code> to detach
187: */
188: public void setElement(final SchemaElement element) {
189: if (this .element != null)
190: this .element.removePropertyChangeListener(wPropL);
191:
192: this .element = element;
193: if (this .element != null) {
194: if (wPropL == null) {
195: propL = new DBElementListener();
196: wPropL = WeakListeners.propertyChange(propL,
197: this .element);
198: } else {
199: // #55249 - need to recreate the listener with the right element
200: wPropL = WeakListeners.propertyChange(propL,
201: this .element);
202: }
203:
204: this .element.addPropertyChangeListener(wPropL);
205: }
206:
207: // change element nodes according to the new element
208: if (nodesInited)
209: refreshKeys();
210: }
211:
212: // other methods ..........................................................................
213:
214: /** Refreshes the keys according to the current state of the element and
215: * filter etc.
216: * (This method is also called when the change of properties occurs either
217: * in the filter or in the element)
218: * PENDING - (for Hanz - should be implemented better, change only the
219: * keys which belong to the changed property).
220: * @param evt the event describing changed property (or null to signalize
221: * that all keys should be refreshed)
222: */
223: public void refreshKeys() {
224: int status;
225:
226: SchemaElement el = getElement();
227:
228: if (parseStatus)
229: status = (el == null) ? SchemaElement.STATUS_ERROR : el
230: .getStatus();
231: else
232: status = (el == null) ? SchemaElement.STATUS_NOT : el
233: .getStatus();
234:
235: switch (status) {
236: case SchemaElement.STATUS_NOT:
237: setKeys(new Object[] { NOT_KEY });
238: break;
239: case SchemaElement.STATUS_ERROR:
240: setKeys(new Object[] { ERROR_KEY });
241: break;
242: case SchemaElement.STATUS_PARTIAL:
243: case SchemaElement.STATUS_OK:
244: refreshAllKeys();
245: break;
246: }
247: }
248:
249: private void refreshKeys2() {
250: setKeys(new Object[] { NOT_KEY });
251: }
252:
253: /** Updates all the keys (elements) according to the current
254: * filter and ordering */
255: private void refreshAllKeys() {
256: int[] order = SchemaElementFilter.DEFAULT_ORDER;
257:
258: final LinkedList keys = new LinkedList();
259: // build ordered and filtered keys for the subelements
260: for (int i = 0; i < order.length; i++)
261: addKeysOfType(keys, order[i]);
262:
263: // set new keys
264: javax.swing.SwingUtilities.invokeLater(new Runnable() {
265: public void run() {
266: // #55249 - first reset to empty set, so the old keys are eliminated.
267: // Due to the way equals() is implemented on schema elements, this needs
268: // to be done: two elements are considered equal if their names are equal,
269: // even if the sets of subelements are not equal
270: setKeys2(Collections.EMPTY_SET);
271: setKeys2(keys);
272: }
273: });
274: }
275:
276: /** Filters and adds the keys of specified type to the given
277: * key collection.
278: */
279: private void addKeysOfType(Collection keys, final int elementType) {
280: SchemaElement schemaElement = (SchemaElement) getElement();
281: if (elementType != 0)
282: keys.add(schemaElement);
283: }
284:
285: private void setKeys2(Collection c) {
286: setKeys(c);
287: }
288:
289: // innerclasses ...........................................................................
290:
291: /** The listener for listening to the property changes in the filter.
292: */
293: private final class DBElementListener implements
294: PropertyChangeListener {
295: public DBElementListener() {
296: }
297:
298: public void propertyChange(PropertyChangeEvent evt) {
299: boolean refresh = DBElementProperties.PROP_SCHEMA
300: .equals(evt.getPropertyName());
301: if (!refresh
302: && DBElementProperties.PROP_STATUS.equals(evt
303: .getPropertyName())) {
304: Integer val = (Integer) evt.getNewValue();
305: refresh = ((val == null) || (val.intValue() != SchemaElement.STATUS_NOT));
306: }
307:
308: if (refresh)
309: javax.swing.SwingUtilities.invokeLater(new Runnable() {
310: public void run() {
311: refreshKeys();
312: }
313: });
314: }
315: } // end of ElementListener inner class
316: }
|