~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to openide/windows/src/org/openide/windows/CloneableTopComponent.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
 
 
42
package org.openide.windows;
 
43
 
 
44
import java.io.Externalizable;
 
45
import java.io.IOException;
 
46
import java.io.ObjectInput;
 
47
import java.io.ObjectInputStream;
 
48
import java.io.ObjectOutput;
 
49
import java.io.Serializable;
 
50
import java.util.Collections;
 
51
import java.util.Enumeration;
 
52
import java.util.HashSet;
 
53
import java.util.Iterator;
 
54
import java.util.Set;
 
55
import org.openide.util.NbBundle;
 
56
import org.openide.util.io.NbMarshalledObject;
 
57
 
 
58
/** A top component which may be cloned.
 
59
* Typically cloning is harmless, i.e. the data contents (if any)
 
60
* of the component are the same, and the new component is merely
 
61
* a different presentation.
 
62
* Also, a list of all cloned components is kept.
 
63
*
 
64
* @author Jaroslav Tulach
 
65
*/
 
66
public abstract class CloneableTopComponent extends TopComponent implements Externalizable, TopComponent.Cloneable {
 
67
    /** generated Serialized Version UID */
 
68
    static final long serialVersionUID = 4893753008783256289L;
 
69
 
 
70
    // say what? --jglick
 
71
 
 
72
    /* Empty set that should save work with testing like
 
73
    * <pre>
 
74
    * if (ref == null || ref.isEmpty ()) {
 
75
    *   CloneableTopComponent c = new CloneableTopComponent (obj);
 
76
    *   ref = c.getReference ();
 
77
    * }
 
78
    * </pre>
 
79
    * Instead one can always set <CODE>ref = Ref.EMPTY</CODE> and test only if
 
80
    * <CODE>ref.isEmpty</CODE> returns <CODE>true</CODE>.
 
81
    */
 
82
 
 
83
    /** Empty clone-sister list.
 
84
    */
 
85
    public static final Ref EMPTY = new Ref();
 
86
 
 
87
    /** reference with list of components */
 
88
    private Ref ref;
 
89
 
 
90
    /** Create a cloneable top component.
 
91
    */
 
92
    public CloneableTopComponent() {
 
93
    }
 
94
 
 
95
    /** Clone the top component and register the clone.
 
96
    * @return the new component
 
97
    */
 
98
    public final Object clone() {
 
99
        return cloneComponent();
 
100
    }
 
101
 
 
102
    /** Clone the top component and register the clone.
 
103
    * Simply calls createClonedObject () and registers the component to
 
104
    * Ref.
 
105
    *
 
106
    * @return the new cloneable top component
 
107
    */
 
108
    public final CloneableTopComponent cloneTopComponent() {
 
109
        CloneableTopComponent top = createClonedObject();
 
110
 
 
111
        // register the component if it has not been registered before
 
112
        top.setReference(getReference());
 
113
 
 
114
        return top;
 
115
    }
 
116
 
 
117
    /** Clone the top component and register the clone.
 
118
    * @return the new component
 
119
    */
 
120
    public final TopComponent cloneComponent() {
 
121
        return cloneTopComponent();
 
122
    }
 
123
 
 
124
    /** Called from {@link #clone} to actually create a new component from this one.
 
125
    * The default implementation serializes and deserializes the original and
 
126
    * returns the result.
 
127
    * Subclasses may leave this as is, assuming they have no special needs for the cloned
 
128
    * data besides copying it from one object to the other. If they do, the superclass
 
129
    * method should be called, and the returned object modified appropriately.
 
130
    * @return a copy of this object
 
131
    */
 
132
    protected CloneableTopComponent createClonedObject() {
 
133
        try {
 
134
            // clones the component using serialization
 
135
            NbMarshalledObject o = new NbMarshalledObject(this);
 
136
            return (CloneableTopComponent) o.get();
 
137
        } catch (IOException ex) {
 
138
            throw new AssertionError(ex);
 
139
        } catch (ClassNotFoundException ex) {
 
140
            throw new AssertionError(ex);
 
141
        }
 
142
    }
 
143
 
 
144
    /** Get a list of all components which are clone-sisters of this one.
 
145
    *
 
146
    * @return the clone registry for this component's group
 
147
    */
 
148
    public synchronized final Ref getReference() {
 
149
        if (ref == null) {
 
150
            ref = new Ref(this);
 
151
        }
 
152
 
 
153
        return ref;
 
154
    }
 
155
 
 
156
    /** Changes the reference to which this components belongs.
 
157
    * @param another the new reference this component should belong
 
158
    */
 
159
    public synchronized final void setReference(Ref another) {
 
160
        if (another == EMPTY) {
 
161
            throw new IllegalArgumentException(
 
162
                NbBundle.getBundle(CloneableTopComponent.class).getString("EXC_CannotAssign")
 
163
            );
 
164
        }
 
165
 
 
166
        if (ref != null) {
 
167
            // Remove from old ref, we are going to belong to 'another' reference.
 
168
            ref.removeComponent(this);
 
169
        }
 
170
 
 
171
        // Register with the new reference.
 
172
        another.register(this);
 
173
 
 
174
        // Finally set the field.
 
175
        ref = another;
 
176
    }
 
177
 
 
178
    /** Overrides superclass method, adds unregistering from references.
 
179
     * @see Ref */
 
180
    protected void componentClosed() {
 
181
        super.componentClosed();
 
182
 
 
183
        if (!isOpened()) {
 
184
            getReference().unregister(this);
 
185
        }
 
186
    }
 
187
 
 
188
    /**
 
189
     * Unregisters this component from its clone list.
 
190
     * {@inheritDoc}
 
191
     */
 
192
    public boolean canClose() {
 
193
        if (!isOpened()) {
 
194
            return false;
 
195
        }
 
196
 
 
197
        return getReference().unregister(this);
 
198
    }
 
199
 
 
200
    @SuppressWarnings("deprecation")
 
201
    public boolean canClose(Workspace workspace, boolean last) {
 
202
        if (last) {
 
203
            return getReference().unregister(this);
 
204
        }
 
205
 
 
206
        return true;
 
207
    }
 
208
 
 
209
    /** Called when the last component in a clone group is closing.
 
210
    * The default implementation just returns <code>true</code>.
 
211
    * Subclasses may specify some hooks to run.
 
212
    * @return <CODE>true</CODE> if the component is ready to be
 
213
    *    closed, <CODE>false</CODE> to cancel
 
214
    */
 
215
    protected boolean closeLast() {
 
216
        return true;
 
217
    }
 
218
 
 
219
    public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException {
 
220
        super.readExternal(oi);
 
221
 
 
222
        if (serialVersion != 0) {
 
223
            // since serialVersion > 0
 
224
            // the reference object is also stored
 
225
            Ref ref = (Ref) oi.readObject();
 
226
 
 
227
            if (ref != null) {
 
228
                setReference(ref);
 
229
            }
 
230
        }
 
231
    }
 
232
 
 
233
    public void writeExternal(ObjectOutput oo) throws java.io.IOException {
 
234
        super.writeExternal(oo);
 
235
 
 
236
        oo.writeObject(ref);
 
237
    }
 
238
 
 
239
    /** Keeps track of a group of sister clones.
 
240
    * <P>
 
241
    * <B>Warning:</B>
 
242
    * For proper use
 
243
    * subclasses should have method readResolve () and implement it
 
244
    * in right way to deal with separate serialization of TopComponent.
 
245
    */
 
246
    public static class Ref implements Serializable {
 
247
        /** generated Serialized Version UID */
 
248
        static final long serialVersionUID = 5543148876020730556L;
 
249
 
 
250
        /** manipulation lock */
 
251
        private static final Object LOCK = new Object();
 
252
 
 
253
        /** Set of registered components. */
 
254
        private transient /*final*/ Set<CloneableTopComponent> componentSet = new HashSet<CloneableTopComponent>(7);
 
255
 
 
256
        /** Default constructor for creating empty reference.
 
257
        */
 
258
        protected Ref() {
 
259
        }
 
260
 
 
261
        /** Constructor.
 
262
        * @param c the component to refer to
 
263
        */
 
264
        private Ref(CloneableTopComponent c) {
 
265
            synchronized (LOCK) {
 
266
                componentSet.add(c);
 
267
            }
 
268
        }
 
269
 
 
270
        /** Enumeration of all registered components.
 
271
        * @return enumeration of CloneableTopComponent
 
272
        */
 
273
        public Enumeration<CloneableTopComponent> getComponents() {
 
274
            Set<CloneableTopComponent> components;
 
275
 
 
276
            synchronized (LOCK) {
 
277
                components = new HashSet<CloneableTopComponent>(componentSet);
 
278
            }
 
279
 
 
280
            return Collections.enumeration(components);
 
281
        }
 
282
 
 
283
        /** Test whether there is any component in this set.
 
284
        * @return <CODE>true</CODE> if the reference set is empty
 
285
        */
 
286
        public boolean isEmpty() {
 
287
            synchronized (LOCK) {
 
288
                return componentSet.isEmpty();
 
289
            }
 
290
        }
 
291
 
 
292
        /** Retrieve an arbitrary component from the set.
 
293
        * @return some component from the list of registered ones
 
294
        * @exception NoSuchElementException if the set is empty
 
295
         * @deprecated Use {@link #getArbitraryComponent} instead.
 
296
         *             It doesn't throw a runtime exception.
 
297
        */
 
298
        public CloneableTopComponent getAnyComponent() {
 
299
            synchronized (LOCK) {
 
300
                return componentSet.iterator().next();
 
301
            }
 
302
        }
 
303
 
 
304
        /** Gets arbitrary component from the set. Preferrably returns currently
 
305
         * active component if found in the set.
 
306
         * @return arbitratry <code>CloneableTopComponent</code> from the set
 
307
         *         or <code>null</code> if the set is empty
 
308
         * @since 3.41 */
 
309
        public CloneableTopComponent getArbitraryComponent() {
 
310
            TopComponent activated = WindowManager.getDefault().getRegistry().getActivated();
 
311
 
 
312
            synchronized (LOCK) {
 
313
                // prefer already active component
 
314
                if (componentSet.contains(activated)) {
 
315
                    return (CloneableTopComponent) activated;
 
316
                }
 
317
 
 
318
                Iterator<CloneableTopComponent> it = componentSet.iterator();
 
319
 
 
320
                if (it.hasNext()) {
 
321
                    return it.next();
 
322
                } else {
 
323
                    return null;
 
324
                }
 
325
            }
 
326
        }
 
327
 
 
328
        /** Register new component.
 
329
        * @param c the component to register
 
330
        */
 
331
        private final void register(CloneableTopComponent c) {
 
332
            synchronized (LOCK) {
 
333
                componentSet.add(c);
 
334
            }
 
335
        }
 
336
 
 
337
        /** Unregister the component. If this is the last asks if it is
 
338
        * allowed to unregister it.
 
339
        *
 
340
        * @param c the component to unregister
 
341
        * @return true if the component agreed to be unregister
 
342
        */
 
343
        private final boolean unregister(CloneableTopComponent c) {
 
344
            int componentCount;
 
345
 
 
346
            synchronized (LOCK) {
 
347
                if (!componentSet.contains(c)) {
 
348
                    return true;
 
349
                }
 
350
 
 
351
                componentCount = componentSet.size();
 
352
            }
 
353
 
 
354
            if ((componentCount > 1) || c.closeLast()) {
 
355
                removeComponent(c);
 
356
 
 
357
                return true;
 
358
            } else {
 
359
                return false;
 
360
            }
 
361
        }
 
362
 
 
363
        private void removeComponent(CloneableTopComponent c) {
 
364
            synchronized (LOCK) {
 
365
                componentSet.remove(c);
 
366
            }
 
367
        }
 
368
 
 
369
        /** Adds also initializing of <code>componentSet</code> field. */
 
370
        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 
371
            in.defaultReadObject();
 
372
 
 
373
            synchronized (LOCK) {
 
374
                componentSet = new HashSet<CloneableTopComponent>(7);
 
375
            }
 
376
        }
 
377
    }
 
378
     // end of Ref
 
379
}