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-2006 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.xam;
043:
044: import java.beans.PropertyChangeListener;
045: import java.beans.PropertyChangeSupport;
046: import java.io.File;
047: import java.io.IOException;
048: import java.lang.ref.WeakReference;
049: import java.util.ArrayList;
050: import java.util.List;
051: import java.util.WeakHashMap;
052: import java.util.logging.Level;
053: import java.util.logging.Logger;
054: import org.netbeans.modules.xml.xam.spi.ModelAccessProvider;
055: import org.openide.util.Lookup;
056: import org.openide.util.RequestProcessor;
057:
058: /**
059: *
060: * @author Chris Webster
061: * @author Nam Nguyen
062: */
063: public abstract class AbstractModelFactory<M extends Model> {
064: public AbstractModelFactory() {
065: factories.add(new WeakReference<AbstractModelFactory>(this ));
066: propSupport = new PropertyChangeSupport(this );
067: }
068:
069: public static final int DELAY_SYNCER = 2000; // milisecs.
070: public static final int DELAY_DIRTY = 1000; // milisecs.
071:
072: public static final String MODEL_LOADED_PROPERTY = "modelLoaded";
073:
074: private WeakHashMap<Object, WeakReference<M>> cachedModels = new WeakHashMap<Object, WeakReference<M>>();
075: private PropertyChangeSupport propSupport;
076:
077: protected abstract M createModel(ModelSource source);
078:
079: /**
080: * Create new model from given model source; returns null if there are errors
081: * during creation.
082: *
083: * Note that, the returned model might not be valid, i.e., source is well-formed.
084: * Also, that the returned model is not cached and does not have background
085: * auto-sync support.
086: */
087: public M createFreshModel(ModelSource modelSource) {
088: M model = createModel(modelSource);
089: try {
090: if (model != null) {
091: model.sync();
092: propSupport.firePropertyChange(MODEL_LOADED_PROPERTY,
093: null, model);
094: }
095: } catch (IOException ioe) {
096: Logger.getLogger(this .getClass().getName()).log(Level.FINE,
097: "Sync has errors", ioe);
098: }
099: return model;
100: }
101:
102: /**
103: * This method extracts the key from the model source. A subclass can
104: * change the ModelSource lookup requirements and thus this method may
105: * be overridden to allow a different key to be used.
106: */
107: protected Object getKey(ModelSource source) {
108: ModelAccessProvider p = getEffectiveAccessProvider(source);
109: if (p != null) {
110: return p.getModelSourceKey(source);
111: }
112: return (File) source.getLookup().lookup(File.class);
113: }
114:
115: private ModelAccessProvider getEffectiveAccessProvider(
116: ModelSource ms) {
117: ModelAccessProvider p = (ModelAccessProvider) ms.getLookup()
118: .lookup(ModelAccessProvider.class);
119: return p == null ? getAccessProvider() : p;
120: }
121:
122: public static ModelAccessProvider getAccessProvider() {
123: return (ModelAccessProvider) Lookup.getDefault().lookup(
124: ModelAccessProvider.class);
125: }
126:
127: protected synchronized M getModel(ModelSource source) {
128: if (source == null) {
129: return null;
130: }
131: Object key = getKey(source);
132: assert key != null;
133: WeakReference<M> modelRef = cachedModels.get(key);
134: M model = (modelRef == null ? null : modelRef.get());
135: if (model == null) {
136: model = createModel(source);
137: if (model != null) {
138: try {
139: model.sync();
140: } catch (IOException ioe) {
141: Logger.getLogger(this .getClass().getName()).log(
142: Level.FINE, "Sync has errors", ioe);
143: }
144: cachedModels.put(key, new WeakReference<M>(model));
145: propSupport.firePropertyChange(MODEL_LOADED_PROPERTY,
146: null, model);
147: }
148: }
149: return model;
150: }
151:
152: private static List<WeakReference<AbstractModelFactory>> factories = new ArrayList<WeakReference<AbstractModelFactory>>();
153:
154: private static RequestProcessor.Task SYNCER = null;
155: static {
156: if (getAccessProvider() != null) {
157: SYNCER = RequestProcessor.getDefault().post(new Runnable() {
158: public void run() {
159: try {
160: for (AbstractModel model : getAllModels()) {
161: if (model.isAutoSyncActive()
162: && model.getAccess()
163: .dirtyIntervalMillis() > DELAY_DIRTY) {
164: model.runAutoSync();
165: }
166: }
167: } catch (Exception e) {
168: Logger.getLogger(getClass().getName()).log(
169: Level.FINE, "auto-sync", e);
170: }
171: SYNCER.schedule(DELAY_SYNCER);
172: }
173: }, DELAY_SYNCER);
174: }
175: }
176:
177: private synchronized static List<AbstractModel> getAllModels() {
178: List<AbstractModel> models = new ArrayList<AbstractModel>();
179: List<WeakReference<AbstractModelFactory>> mfactories = new ArrayList<WeakReference<AbstractModelFactory>>(
180: factories);
181:
182: for (WeakReference<AbstractModelFactory> r : mfactories) {
183: AbstractModelFactory factory = r.get();
184: if (factory != null) {
185: for (Object m : factory.getModels()) {
186: if (m instanceof AbstractModel) {
187: AbstractModel am = (AbstractModel) m;
188: models.add(am);
189: }
190: }
191: }
192: }
193: return models;
194: }
195:
196: /**
197: * Returns list of models currently cached by the factory.
198: */
199: public List<M> getModels() {
200: List<WeakReference<M>> refs;
201: synchronized (this ) {
202: refs = new ArrayList<WeakReference<M>>(cachedModels
203: .values());
204: }
205:
206: List<M> ret = new ArrayList<M>();
207: for (WeakReference<M> ref : refs) {
208: if (ref != null) {
209: M model = ref.get();
210: if (model != null) {
211: ret.add(model);
212: }
213: }
214: }
215: return ret;
216: }
217:
218: /**
219: * Adds property change listener on the factory to be notified on
220: * when new models are loaded by the factory.
221: */
222: public void addPropertyChangeListener(PropertyChangeListener l) {
223: propSupport.addPropertyChangeListener(l);
224: }
225:
226: /**
227: * Removes property change listener.
228: */
229: public void removePropertyChangeListener(PropertyChangeListener l) {
230: propSupport.removePropertyChangeListener(l);
231: }
232: }
|