2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
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]"
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.
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.
42
package org.openide.windows;
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;
55
import org.openide.util.NbBundle;
56
import org.openide.util.io.NbMarshalledObject;
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.
64
* @author Jaroslav Tulach
66
public abstract class CloneableTopComponent extends TopComponent implements Externalizable, TopComponent.Cloneable {
67
/** generated Serialized Version UID */
68
static final long serialVersionUID = 4893753008783256289L;
72
/* Empty set that should save work with testing like
74
* if (ref == null || ref.isEmpty ()) {
75
* CloneableTopComponent c = new CloneableTopComponent (obj);
76
* ref = c.getReference ();
79
* Instead one can always set <CODE>ref = Ref.EMPTY</CODE> and test only if
80
* <CODE>ref.isEmpty</CODE> returns <CODE>true</CODE>.
83
/** Empty clone-sister list.
85
public static final Ref EMPTY = new Ref();
87
/** reference with list of components */
90
/** Create a cloneable top component.
92
public CloneableTopComponent() {
95
/** Clone the top component and register the clone.
96
* @return the new component
98
public final Object clone() {
99
return cloneComponent();
102
/** Clone the top component and register the clone.
103
* Simply calls createClonedObject () and registers the component to
106
* @return the new cloneable top component
108
public final CloneableTopComponent cloneTopComponent() {
109
CloneableTopComponent top = createClonedObject();
111
// register the component if it has not been registered before
112
top.setReference(getReference());
117
/** Clone the top component and register the clone.
118
* @return the new component
120
public final TopComponent cloneComponent() {
121
return cloneTopComponent();
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
132
protected CloneableTopComponent createClonedObject() {
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);
144
/** Get a list of all components which are clone-sisters of this one.
146
* @return the clone registry for this component's group
148
public synchronized final Ref getReference() {
156
/** Changes the reference to which this components belongs.
157
* @param another the new reference this component should belong
159
public synchronized final void setReference(Ref another) {
160
if (another == EMPTY) {
161
throw new IllegalArgumentException(
162
NbBundle.getBundle(CloneableTopComponent.class).getString("EXC_CannotAssign")
167
// Remove from old ref, we are going to belong to 'another' reference.
168
ref.removeComponent(this);
171
// Register with the new reference.
172
another.register(this);
174
// Finally set the field.
178
/** Overrides superclass method, adds unregistering from references.
180
protected void componentClosed() {
181
super.componentClosed();
184
getReference().unregister(this);
189
* Unregisters this component from its clone list.
192
public boolean canClose() {
197
return getReference().unregister(this);
200
@SuppressWarnings("deprecation")
201
public boolean canClose(Workspace workspace, boolean last) {
203
return getReference().unregister(this);
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
215
protected boolean closeLast() {
219
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException {
220
super.readExternal(oi);
222
if (serialVersion != 0) {
223
// since serialVersion > 0
224
// the reference object is also stored
225
Ref ref = (Ref) oi.readObject();
233
public void writeExternal(ObjectOutput oo) throws java.io.IOException {
234
super.writeExternal(oo);
239
/** Keeps track of a group of sister clones.
243
* subclasses should have method readResolve () and implement it
244
* in right way to deal with separate serialization of TopComponent.
246
public static class Ref implements Serializable {
247
/** generated Serialized Version UID */
248
static final long serialVersionUID = 5543148876020730556L;
250
/** manipulation lock */
251
private static final Object LOCK = new Object();
253
/** Set of registered components. */
254
private transient /*final*/ Set<CloneableTopComponent> componentSet = new HashSet<CloneableTopComponent>(7);
256
/** Default constructor for creating empty reference.
262
* @param c the component to refer to
264
private Ref(CloneableTopComponent c) {
265
synchronized (LOCK) {
270
/** Enumeration of all registered components.
271
* @return enumeration of CloneableTopComponent
273
public Enumeration<CloneableTopComponent> getComponents() {
274
Set<CloneableTopComponent> components;
276
synchronized (LOCK) {
277
components = new HashSet<CloneableTopComponent>(componentSet);
280
return Collections.enumeration(components);
283
/** Test whether there is any component in this set.
284
* @return <CODE>true</CODE> if the reference set is empty
286
public boolean isEmpty() {
287
synchronized (LOCK) {
288
return componentSet.isEmpty();
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.
298
public CloneableTopComponent getAnyComponent() {
299
synchronized (LOCK) {
300
return componentSet.iterator().next();
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
309
public CloneableTopComponent getArbitraryComponent() {
310
TopComponent activated = WindowManager.getDefault().getRegistry().getActivated();
312
synchronized (LOCK) {
313
// prefer already active component
314
if (componentSet.contains(activated)) {
315
return (CloneableTopComponent) activated;
318
Iterator<CloneableTopComponent> it = componentSet.iterator();
328
/** Register new component.
329
* @param c the component to register
331
private final void register(CloneableTopComponent c) {
332
synchronized (LOCK) {
337
/** Unregister the component. If this is the last asks if it is
338
* allowed to unregister it.
340
* @param c the component to unregister
341
* @return true if the component agreed to be unregister
343
private final boolean unregister(CloneableTopComponent c) {
346
synchronized (LOCK) {
347
if (!componentSet.contains(c)) {
351
componentCount = componentSet.size();
354
if ((componentCount > 1) || c.closeLast()) {
363
private void removeComponent(CloneableTopComponent c) {
364
synchronized (LOCK) {
365
componentSet.remove(c);
369
/** Adds also initializing of <code>componentSet</code> field. */
370
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
371
in.defaultReadObject();
373
synchronized (LOCK) {
374
componentSet = new HashSet<CloneableTopComponent>(7);