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-2007 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.xml.schema.ui.nodes;
043:
044: import java.util.ArrayList;
045: import java.util.List;
046: import javax.swing.event.ChangeEvent;
047: import javax.swing.event.ChangeListener;
048: import org.netbeans.modules.xml.schema.model.SchemaComponent;
049: import org.netbeans.modules.xml.schema.ui.nodes.categorized.CategoryNode;
050: import org.netbeans.modules.xml.xam.Model;
051: import org.openide.nodes.Children;
052: import org.openide.nodes.Index;
053: import org.openide.nodes.Node;
054:
055: /**
056: * Represents a Children that can be rebuilt on demand. Also implements
057: * the Index cookie to allow re-ordering of the children. To enable, the
058: * instance must be added to the lookup (cookie set) of the parent Node.
059: *
060: * @author Todd Fast, todd.fast@sun.com
061: * @author Nathan Fiedler
062: */
063: public abstract class RefreshableChildren extends Children.Keys
064: implements Index {
065: /** Permits changing order of children. */
066: private Index indexSupport;
067:
068: /**
069: * Creates a new instance of RefreshableChildren.
070: */
071: public RefreshableChildren() {
072: super ();
073: indexSupport = new IndexSupport();
074: }
075:
076: /**
077: * Refresh the children.
078: */
079: public abstract void refreshChildren();
080:
081: public void addChangeListener(ChangeListener l) {
082: indexSupport.addChangeListener(l);
083: }
084:
085: public void removeChangeListener(ChangeListener l) {
086: indexSupport.removeChangeListener(l);
087: }
088:
089: public void exchange(int x, int y) {
090: indexSupport.exchange(x, y);
091: }
092:
093: public int indexOf(Node node) {
094: return indexSupport.indexOf(node);
095: }
096:
097: public void moveUp(int i) {
098: indexSupport.moveUp(i);
099: }
100:
101: public void moveDown(int i) {
102: indexSupport.moveDown(i);
103: }
104:
105: public void move(int x, int y) {
106: indexSupport.move(x, y);
107: }
108:
109: public void reorder() {
110: indexSupport.reorder();
111: }
112:
113: public void reorder(int[] i) {
114: indexSupport.reorder(i);
115: }
116:
117: /**
118: * Allows re-ordering of the child nodes.
119: */
120: private class IndexSupport extends Index.Support {
121:
122: public void reorder(int[] perm) {
123: // Moving the last node of five to the second position results
124: // in an array that looks like: [0, 2, 3, 4, 1]
125: // This means that the first node stays first, the second
126: // node is now third, and so on, while the last node is
127: // now in the second position.
128:
129: // Because some nodes present the children of their only child
130: // (e.g. simple type node), we need to get the node children
131: // and ask the first one for its schema component. We assume
132: // there is at least one child (otherwise this method would not
133: // be invoked) and that all of the children have a common parent.
134: Node[] nodes = getNodes();
135: SchemaComponentNode scn = (SchemaComponentNode) nodes[0]
136: .getCookie(SchemaComponentNode.class);
137: SchemaComponent parent = null;
138: if (scn != null) {
139: parent = scn.getReference().get().getParent();
140: } else {
141: // Not a schema component node? May be a category node.
142: CategoryNode cn = (CategoryNode) getNode().getCookie(
143: CategoryNode.class);
144: if (cn != null) {
145: parent = cn.getReference().get();
146: }
147: // Else, it is unknown and we cannot reorder its children.
148: }
149: if (parent != null) {
150: // Re-order the children in the model and let the nodes get
151: // refreshed via the listeners.
152: Model model = parent.getModel();
153: try {
154: model.startTransaction();
155: List<SchemaComponent> children = parent
156: .getChildren();
157: // Need to create a copy of the list since we would
158: // otherwise be mutating it locally and via the model.
159: children = new ArrayList<SchemaComponent>(children);
160: SchemaComponent[] arr = children
161: .toArray(new SchemaComponent[children
162: .size()]);
163: for (int i = 0; i < arr.length; i++) {
164: children.set(perm[i], arr[i]);
165: }
166: // Make copies of the children. Need to make a copy,
167: // otherwise model says we are adding a node that is
168: // already a part of the tree.
169: List<SchemaComponent> copies = new ArrayList<SchemaComponent>();
170: for (SchemaComponent child : children) {
171: copies
172: .add((SchemaComponent) child
173: .copy(parent));
174: }
175: // Cannot remove children until after they are copied.
176: for (SchemaComponent child : children) {
177: model.removeChildComponent(child);
178: }
179: // Now add the copies back to the parent.
180: for (SchemaComponent copy : copies) {
181: model.addChildComponent(parent, copy, -1);
182: }
183: } catch (IndexOutOfBoundsException ioobe) {
184: // This occurs for redefine node when user drags and drops.
185: // Need to silently fail, as with reordering category nodes.
186: return;
187: } finally {
188: model.endTransaction();
189: }
190: // Notify listeners of the change.
191: fireChangeEvent(new ChangeEvent(this ));
192: }
193: // Else silently fail, as with reordering within category nodes.
194: }
195:
196: public int getNodesCount() {
197: return RefreshableChildren.this .getNodesCount();
198: }
199:
200: public Node[] getNodes() {
201: return RefreshableChildren.this.getNodes();
202: }
203: }
204: }
|