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.netbeans.modules.settings;
45
import java.io.IOException;
46
import java.util.logging.Level;
47
import java.util.logging.Logger;
49
import org.openide.cookies.SaveCookie;
50
import org.openide.filesystems.FileSystem;
51
import org.openide.filesystems.FileObject;
53
import org.netbeans.spi.settings.Convertor;
54
import org.netbeans.spi.settings.Saver;
55
import org.openide.util.Exceptions;
57
/** Support handles automatic storing/upgrading; notifies about changes in file.
59
* @author Jan Pokorsky
61
final class SaveSupport {
62
/** property means setting is changed and should be changed */
63
public final static String PROP_SAVE = "savecookie"; //NOI18N
64
/** property means setting file content is changed */
65
public final static String PROP_FILE_CHANGED = "fileChanged"; //NOI18N
66
/** data object name cached in the attribute to prevent instance creation when
67
* its node is displayed.
68
* @see org.openide.loaders.InstanceDataObject#EA_NAME
70
static final String EA_NAME = "name"; // NOI18N
72
/** support for PropertyChangeListeners */
73
private PropertyChangeSupport changeSupport;
75
/** convertor for possible format upgrade */
76
private Convertor convertor;
77
/** SaveCookie implementation */
78
private final SaveCookieImpl instToSave = new SaveCookieImpl();
79
/** setting is already changed */
80
private boolean isChanged = false;
82
private final FileObject file;
83
/** reference to setting object */
84
private final java.lang.ref.SoftReference<Object> instance;
85
private final InstanceProvider ip;
86
/** remember whether the DataObject is a template or not; calling isTemplate() is slow */
87
private Boolean knownToBeTemplate = null;
89
/** Creates a new instance of SaveSupport
90
* @param ip instance provider
91
* @param inst setting object
93
public SaveSupport(InstanceProvider ip, Object inst) {
95
this.instance = new java.lang.ref.SoftReference<Object>(inst);
96
this.file = ip.getFile();
99
/** get convertor for possible upgrade; can be null */
100
private Convertor getConvertor() {
104
/** try to find out convertor for possible upgrade and cache it; can be null */
105
private Convertor initConvertor() {
106
Object inst = instance.get();
108
throw new IllegalStateException("setting object cannot be null: " + ip);// NOI18N
112
FileObject newProviderFO = Env.findProvider(inst.getClass());
113
if (newProviderFO != null) {
114
if (getPublicID(newProviderFO).equals(getPublicID(ip.getProvider()))) {
115
// nothing to upgrade
116
convertor = ip.getConvertor();
119
Object attrb = newProviderFO.getAttribute(Env.EA_CONVERTOR);
120
if (attrb == null || !(attrb instanceof Convertor)) {
121
throw new IOException("cannot create convertor: " + attrb + ", provider: " + newProviderFO); //NOI18N
123
convertor = (Convertor) attrb;
127
convertor = ip.getConvertor();
128
} catch (IOException ex) {
129
Logger.getLogger(SaveSupport.class.getName()).log(Level.WARNING, null, ex);
134
/** get publicid of the file fo */
135
private String getPublicID(FileObject fo) throws IOException {
136
FileObject foEntity = Env.findEntityRegistration(fo);
137
if (foEntity == null) foEntity = fo;
138
Object publicId = foEntity.getAttribute(Env.EA_PUBLICID);
139
if (publicId == null || !(publicId instanceof String)) {
140
throw new IOException("missing or invalid attribute: " + //NOI18N
141
Env.EA_PUBLICID + ", provider: " + foEntity); //NOI18N
143
return (String) publicId;
146
/** return SaveCookie impl */
147
public final SaveCookie getSaveCookie () {
151
/** is setting object changed? */
152
public final boolean isChanged() {
156
/** Registers PropertyChangeListener to receive events; initialize
157
* listening to events comming from the setting object and file object.
158
* @param listener The listener to register.
160
public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
161
if (changeSupport == null) {
162
changeSupport = new PropertyChangeSupport(this);
163
Object inst = instance.get();
164
if (inst == null) return;
165
Convertor conv = initConvertor();
167
conv.registerSaver(inst, instToSave);
170
changeSupport.addPropertyChangeListener(listener);
173
/** Removes PropertyChangeListener from the list of listeners.
174
* @param listener The listener to remove.
176
public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
177
if (changeSupport != null) {
178
changeSupport.removePropertyChangeListener(listener);
179
Object inst = instance.get();
180
if (inst == null) return;
181
Convertor conv = getConvertor();
183
conv.unregisterSaver(inst, instToSave);
188
/** Notifies all registered listeners about the event.
189
* @param event The event to be fired
190
* @see #PROP_FILE_CHANGED
193
private void firePropertyChange(String name) {
194
if (changeSupport != null)
195
changeSupport.firePropertyChange(name, null, null);
198
/** called by ScheduledRequest in order to perform the request */
199
public void writeDown() throws IOException {
200
instToSave.writeDown();
203
/** Support for storing instances allowing identify the origin of file events
204
* fired as a consequence of this storing.
206
private class SaveCookieImpl implements FileSystem.AtomicAction, SaveCookie, Saver {
208
private java.io.ByteArrayOutputStream buf;
210
private SaveCookieImpl() {
213
/** store buffer to the file. */
214
public void run () throws IOException {
215
if (!ip.getDataObject().isValid()) {
216
//invalid data object cannot be used for storing
217
Logger.getAnonymousLogger().fine("invalid data object cannot be used for storing " + ip.getDataObject()); // NOI18N
220
org.openide.filesystems.FileLock lock = null;
221
java.io.OutputStream los;
222
synchronized (ip.READWRITE_LOCK) {
223
// if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
224
// err.log("saving " + dobj); // NOI18N
226
lock = ip.getScheduledRequest().getFileLock();
227
if (lock == null) return;
228
los = file.getOutputStream(lock);
230
java.io.OutputStream os = new java.io.BufferedOutputStream(los, 1024);
233
// if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
234
// dobj.err.log("saved " + dobj); // NOI18N
242
/** Implementation of SaveCookie. */
243
public void save() throws IOException {
244
if (!isChanged) return;
245
ip.getScheduledRequest().runAndWait();
248
private void writeDown() throws IOException {
249
Object inst = instance.get();
250
if (inst == null) return ;
251
Convertor conv = getConvertor();
252
if (conv == null) return ;
253
java.io.ByteArrayOutputStream b = new java.io.ByteArrayOutputStream(1024);
254
java.io.Writer w = ContextProvider.createWriterContextProvider(
255
new java.io.OutputStreamWriter(b, "UTF-8"), // NOI18N
256
SaveSupport.this.file
266
file.getFileSystem().runAtomicAction(this);
268
synchronizeName(inst);
269
if (!isChanged) firePropertyChange(PROP_SAVE);
272
public void markDirty() {
273
if (isChanged || !ip.getDataObject().isValid()) return;
274
if (knownToBeTemplate == null) knownToBeTemplate = ip.getDataObject().isTemplate() ? Boolean.TRUE : Boolean.FALSE;
275
if (knownToBeTemplate.booleanValue()) return;
277
firePropertyChange(PROP_SAVE);
280
public void requestSave() throws java.io.IOException {
281
if (isChanged || !ip.getDataObject().isValid()) return;
282
if (knownToBeTemplate == null) knownToBeTemplate = ip.getDataObject().isTemplate() ? Boolean.TRUE : Boolean.FALSE;
283
if (knownToBeTemplate.booleanValue()) return;
285
firePropertyChange(PROP_SAVE);
286
ip.getScheduledRequest().schedule(instance.get());
289
/** try to synchronize file name with instance name */
290
private void synchronizeName(Object inst) {
291
java.lang.reflect.Method getter;
294
getter = inst.getClass().getMethod("getDisplayName"); // NOI18N
295
} catch (NoSuchMethodException me) {
296
getter = inst.getClass().getMethod("getName"); // NOI18N
298
} catch (Exception ex) { // do nothing
301
if (!getter.isAccessible()) return;
304
String name = (String) getter.invoke(inst);
305
String oldName = ip.getDataObject().getName();
306
if (!name.equals(oldName)) {
307
file.setAttribute(EA_NAME, name);
308
} else if (file.getAttribute(EA_NAME) == null) {
309
file.setAttribute(EA_NAME, name);
311
} catch (Exception ex) {
312
Exceptions.attachLocalizedMessage(ex, file.toString());
313
Logger.getLogger(SaveSupport.class.getName()).log(Level.WARNING, null, ex);