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 2002-2003 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.netbeans.modules.settings;
44
import java.beans.PropertyChangeEvent;
45
import java.lang.ref.SoftReference;
46
import java.io.IOException;
47
import java.util.logging.Level;
48
import java.util.logging.Logger;
49
import org.netbeans.modules.settings.convertors.SerialDataNode;
51
import org.openide.filesystems.FileObject;
52
import org.openide.filesystems.FileSystem;
53
import org.openide.filesystems.FileUtil;
54
import org.openide.nodes.Node;
55
import org.openide.util.Lookup;
56
import org.openide.cookies.SaveCookie;
57
import org.openide.cookies.InstanceCookie;
59
import org.netbeans.spi.settings.Convertor;
60
import org.openide.util.Exceptions;
62
/** Provides the Lookup content.
64
* @author Jan Pokorsky
66
final class InstanceProvider extends org.openide.filesystems.FileChangeAdapter
67
implements java.beans.PropertyChangeListener, FileSystem.AtomicAction {
68
/** Logging for events in this class */
69
private static final Logger LOG = Logger.getLogger(InstanceProvider.class.getName()); // NOI18N
71
/** container handling objects provided by {@link #lookup} */
72
private final org.openide.util.lookup.InstanceContent lkpContent;
73
/** container exposing setting to the outside world */
74
private final org.openide.util.Lookup lookup;
75
private final org.openide.loaders.DataObject dobj;
76
private final FileObject settingFO;
77
private final FileObject providerFO;
78
private final NodeConvertor node;
80
private SaveSupport saver;
81
private SaveCookie scCache;
82
private boolean wasReportedProblem = false;
83
private java.util.Set<String> instanceOfSet;
84
private String instanceClassName;
85
/** lock used to sync read/write operations for .settings file */
86
final Object READWRITE_LOCK = new Object();
88
/** Creates a new instance of InstanceCooikeProvider */
89
public InstanceProvider(org.openide.loaders.DataObject dobj, FileObject providerFO) {
90
// System.out.println("new IP: " + dobj);
91
this.settingFO = dobj.getPrimaryFile();
92
this.providerFO = providerFO;
95
settingFO.addFileChangeListener(
96
FileUtil.weakFileChangeListener(this, settingFO));
98
lkpContent = new org.openide.util.lookup.InstanceContent();
99
lkpContent.add(createInstance(null));
100
node = new NodeConvertor();
101
lkpContent.add(this, node);
102
lookup = new org.openide.util.lookup.AbstractLookup(lkpContent);
105
/** provides content like InstanceCookie, SaveCokie */
106
public Lookup getLookup() {
109
/** file contanining various attributes related to setting like convertor
112
FileObject getProvider() {
115
/** file containing a persisted setting object */
116
FileObject getFile () {
120
org.openide.loaders.DataObject getDataObject() {
124
public void propertyChange(PropertyChangeEvent evt) {
125
if (evt == null) return;
127
String name = evt.getPropertyName();
130
else if (name == SaveSupport.PROP_SAVE)
132
else if (name == SaveSupport.PROP_FILE_CHANGED) {
133
synchronized (this) {
134
instanceOfSet = null;
136
instanceCookieChanged(null);
140
/** process events coming from the file object*/
141
public void fileChanged(org.openide.filesystems.FileEvent fe) {
142
if (saver != null && fe.firedFrom((FileSystem.AtomicAction) saver.getSaveCookie())) return;
143
propertyChange(new PropertyChangeEvent(this, SaveSupport.PROP_FILE_CHANGED, null, null));
146
public void fileDeleted(org.openide.filesystems.FileEvent fe) {
147
if (saver != null && fe.firedFrom((FileSystem.AtomicAction) saver.getSaveCookie())) return;
152
/** allow to listen on changes of the object inst; should be called when
153
* new instance is created */
154
private synchronized void attachToInstance(Object inst) {
156
saver.removePropertyChangeListener(this);
157
getScheduledRequest().forceToFinish();
159
saver = createSaveSupport(inst);
160
saver.addPropertyChangeListener(this);
163
/** create own InstanceCookie implementation */
164
private InstanceCookie.Of createInstance(Object inst) {
165
return new InstanceCookieImpl(inst);
168
/** method provides a support storing the setting */
169
private SaveSupport createSaveSupport(Object inst) {
170
return new SaveSupport(this, inst);
173
private void provideSaveCookie() {
174
SaveCookie scNew = saver.getSaveCookie();
175
if (scCache != null) {
176
if (!saver.isChanged()) {
177
if (LOG.isLoggable(Level.FINE)) LOG.fine("remove save cookie: " + dobj); // NOI18N
178
lkpContent.remove(scCache);
183
if (saver.isChanged()) {
185
if (LOG.isLoggable(Level.FINE)) LOG.fine("add save cookie: " + dobj + " cookie: " + scNew); // NOI18N
186
lkpContent.add(scNew);
192
private void releaseInstance() {
193
SaveSupport _saver = saver;
194
if (_saver != null) {
195
_saver.removePropertyChangeListener(this);
198
if (scCache != null) {
199
if (LOG.isLoggable(Level.FINE)) LOG.fine("release instance and remove save cookie: " + dobj); // NOI18N
200
lkpContent.remove(scCache);
201
getScheduledRequest().cancel();
205
lkpContent.remove(this, node);
208
private void instanceCookieChanged(Object inst) {
209
if (LOG.isLoggable(Level.FINE)) LOG.fine("instanceCookieChanged: " + dobj + " inst: " + inst); // NOI18N
212
lkpContent.add(this, node);
214
Object ic = lookup.lookup(InstanceCookie.class);
215
lkpContent.remove(ic);
217
Object newCookie = createInstance(inst);
218
lkpContent.add(newCookie);
219
if (LOG.isLoggable(Level.FINE)) LOG.fine("cookie replaced: " + dobj + " old: " + ic + " new: " + newCookie); // NOI18N
222
private Convertor convertor;
224
/** find out proper convertor */
225
Convertor getConvertor() throws IOException {
226
if (convertor == null) {
227
Object attrb = providerFO.getAttribute(Env.EA_CONVERTOR);
228
if (attrb == null || !(attrb instanceof Convertor)) {
229
throw new IOException("cannot create convertor: " + attrb + ", provider:" +providerFO); //NOI18N
231
convertor = (Convertor) attrb;
236
/** find out setting object class name */
237
private synchronized String getInstanceClassName() {
238
if (instanceClassName == null) {
239
Object name = providerFO.getAttribute(Env.EA_INSTANCE_CLASS_NAME);
240
if (name != null && name instanceof String) {
241
instanceClassName = org.openide.util.Utilities.translate((String) name);
243
instanceClassName = null;
246
return instanceClassName;
249
public String toString() {
250
return this.getClass().getName() + '@' +
251
Integer.toHexString(System.identityHashCode(this)) +
252
'[' + getDataObject() + ", " + getProvider() + ']';
256
/** called by ScheduledRequest in order to perform the request */
257
public void run() throws IOException {
261
/** scheduled request to store setting */
262
private ScheduledRequest request;
264
/** get the scheduled request to store setting */
265
synchronized ScheduledRequest getScheduledRequest() {
266
if (request == null) {
267
request = new ScheduledRequest(settingFO, this);
272
/////////////////////////////////////////////////////////////////////////
273
// InstanceCookieImpl
274
/////////////////////////////////////////////////////////////////////////
276
/** InstanceCookie implementation. */
277
final class InstanceCookieImpl implements InstanceCookie.Of {
278
private SoftReference<Object> cachedInstance;// = new SoftReference(null);
280
public InstanceCookieImpl(Object inst) {
281
setCachedInstance(inst);
284
public Class instanceClass() throws IOException, ClassNotFoundException {
285
String name = getInstanceClassName();
287
return instanceCreate().getClass();
289
return ((ClassLoader)Lookup.getDefault().lookup(ClassLoader.class)).loadClass(name);
293
public Object instanceCreate() throws java.io.IOException, ClassNotFoundException {
296
synchronized (this) {
297
inst = getCachedInstance();
298
if (inst != null) return inst;
302
synchronized (READWRITE_LOCK) {
303
java.io.Reader r = ContextProvider.createReaderContextProvider(
304
new java.io.InputStreamReader(settingFO.getInputStream(),"UTF-8"), //NOI18N
307
inst = getConvertor().read(r);
309
} catch (IOException ex) {
310
throw (IOException) Exceptions.attachLocalizedMessage(ex,
311
InstanceProvider.this.toString());
312
} catch (ClassNotFoundException ex) {
313
throw (ClassNotFoundException) Exceptions.attachLocalizedMessage(ex,
314
InstanceProvider.this.toString());
317
synchronized (this) {
318
Object existing = getCachedInstance();
319
if (existing != null) return existing;
320
setCachedInstance(inst);
322
attachToInstance(inst);
327
public String instanceName() {
328
String name = getInstanceClassName();
329
if (name != null) return name;
333
return instanceClass().getName();
334
} catch (IOException ex) {
336
} catch (ClassNotFoundException ex) {
339
if (e != null && !wasReportedProblem) {
340
wasReportedProblem = true;
341
Exceptions.attachLocalizedMessage(e, dobj.toString());
342
Logger.getLogger(InstanceProvider.class.getName()).log(Level.WARNING, null, e);
344
return "Unknown"; // NOI18N
347
public boolean instanceOf(Class<?> type) {
348
synchronized (InstanceProvider.this) {
349
if (instanceOfSet == null) {
350
instanceOfSet = Env.parseAttribute(providerFO.getAttribute(Env.EA_INSTANCE_OF));
351
java.util.Iterator<String> it = instanceOfSet.iterator();
352
instanceOfSet = new java.util.HashSet<String>(instanceOfSet.size() * 5 / 4);
353
while (it.hasNext()) {
354
instanceOfSet.add(org.openide.util.Utilities.translate(it.next()));
358
if (instanceOfSet.isEmpty()) {
361
return type.isAssignableFrom(instanceClass());
362
} catch (IOException ex) {
364
} catch (ClassNotFoundException ex) {
367
if (e != null && !wasReportedProblem) {
368
wasReportedProblem = true;
369
Exceptions.attachLocalizedMessage(e, dobj.toString());
370
Logger.getLogger(InstanceProvider.class.getName()).log(Level.WARNING, null, e);
374
return instanceOfSet.contains(type.getName());
378
// called by InstanceDataObject to set new object
379
public void setInstance(Object inst, boolean save) throws IOException {
380
instanceCookieChanged(inst);
382
attachToInstance(inst);
383
if (save) getScheduledRequest().runAndWait();
387
private Object getCachedInstance() {
388
return cachedInstance.get();
390
private void setCachedInstance(Object inst) {
391
cachedInstance = new SoftReference<Object>(inst);
395
////////////////////////////////////////////////////////////////////////////
397
////////////////////////////////////////////////////////////////////////////
399
/** allow to postpone the node creation */
400
private static final class NodeConvertor
401
implements org.openide.util.lookup.InstanceContent.Convertor<InstanceProvider, Node> {
404
public Node convert(InstanceProvider o) {
405
return new SerialDataNode(o.getDataObject());
408
public Class<Node> type(InstanceProvider o) {
412
public String id(InstanceProvider o) {
413
// Generally irrelevant in this context.
417
public String displayName(InstanceProvider o) {
418
// Again, irrelevant here.