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: package org.netbeans.modules.vmd.api.model;
042:
043: /**
044: * This presenter is designed to allow receiving notification about document changes.
045: * <p>
046: * When a presenter is really attached to a component, then notifyAttached method is called to notify it. Therefore
047: * perform all initialization inside that method. Similarly notifyDetached is called when a presenter is dettached
048: * from a component.
049: * <p>
050: * When a design is changed, designChanged method is called on a presenter. The getEventFilter method is called once
051: * after notifyAttached method is called, for resolving the filter that is used for the design-changed listening.
052: * If the presenter changes internally (based on design-changed or other way), it has to call firePresenterChanged method
053: * to notify the model about it.
054: * <p>
055: * It is possible to use have a dependencies between presenters e.g. a presenter requires additional data
056: * from other presenter for its own work. For that purpose there are addDependency and removeDependency methods that could
057: * be called from notifyAttached and notifyDetached. When a presenter is no longer used, that all its dependencies are
058: * automatically removed.
059: * <p>
060: * When at least one of the dependent presenters (those that this presenter has a dependency on), is changed, presenterChanged
061: * method is called. At the time of the method call, presenterChanged methods on all the dependent presenters are called
062: * (this means all dependent presenters are resolved).
063: * When a presenter fires a presenter-changed event, the presenterChanged method is called automatically called too.
064: *
065: * @author David Kaspar
066: */
067: public abstract class DynamicPresenter extends Presenter {
068:
069: // TODO - right now a dependency could be set only in notifyAttached method - later there could be a need for doing it in designChanged and presenterChanged methods too.
070:
071: private Listener listener; // once it is set to non-null value, it must not be set to null, because removeDependency/autoRemoveAllDependecies will not be working.
072: private State state;
073:
074: // private long lastUpdateEventID;
075:
076: /**
077: * Creates a new dynamic presenter
078: */
079: protected DynamicPresenter() {
080: state = State.DISABLED;
081: // lastUpdateEventID = Long.MIN_VALUE;
082: }
083:
084: final PresenterListener getPresenterListener() {
085: return listener;
086: }
087:
088: // NOTE - if you modify this method, look at Presenter.setNotifyAttached method too
089: @Override
090: final void setNotifyAttached(DesignComponent component) {
091: super .setNotifyAttached(component);
092:
093: state = State.ADDING_DEPENDENCIES;
094: notifyAttached(component);
095: state = State.DISABLED;
096: DesignEventFilter filter = getEventFilter();
097: if (filter != null) {
098: if (listener == null)
099: listener = new Listener();
100: component.getDocument().getListenerManager()
101: .addDesignListener(listener, filter);
102: }
103: }
104:
105: // NOTE - if you modify this method, look at Presenter.setNotifyDetached method too
106: @Override
107: final void setNotifyDetached(DesignComponent component) {
108: super .setNotifyDetached(component);
109:
110: if (listener != null)
111: component.getDocument().getListenerManager()
112: .removeDesignListener(listener);
113: state = State.REMOVING_DEPENDENCIES;
114: notifyDetached(component);
115: state = State.DISABLED;
116: }
117:
118: /**
119: * This method is called when the presenter is attached to a component. Do the initialization and set dependencies.
120: * @param component the component
121: */
122: protected abstract void notifyAttached(DesignComponent component);
123:
124: /**
125: * This method is called when the presenter is dettached from a component. The presenter will be no longer used.
126: * Do the finalization and unset dependencies. Unsetting dependencies is optional because it is done automatically.
127: * @param component the component
128: */
129: protected abstract void notifyDetached(DesignComponent component);
130:
131: /**
132: * This method is called to obtain a event filter for calling designChanged callback method of this presenter.
133: * <p/>
134: * Note: This method is called after notifyAttached method is performed and it is called only once.
135: * The filter class is mutable and could be changed during the presenter life-time.
136: * @return the filter
137: */
138: protected abstract DesignEventFilter getEventFilter();
139:
140: // /**
141: // * Checks and sets a flag that a specific event was processed. Call this method in updateSelf method to check
142: // * whether the presenter has to update its data from a model or whether it is already done.
143: // * @param event the event
144: // * @return true if the event was alreay processed
145: // */
146: // protected final boolean checkSetEvent (DesignEvent event) {
147: // long eventID = event.getEventID ();
148: // if (eventID <= lastUpdateEventID)
149: // return true;
150: // lastUpdateEventID = eventID;
151: // return false;
152: // }
153:
154: /**
155: * This method is called when a design is changed according to the event filter.
156: * The presenter should update its data that could be resolved from the model directly without communication with other presenters.
157: * When data are change, firePresenterChanged method should be called to notify others about it
158: * (this is required for correct work of presenter dependencies).
159: * @param event the design-changed event
160: */
161: protected abstract void designChanged(DesignEvent event);
162:
163: /**
164: * This method is called when at least one of dependent presenters are changed. At the time of the method call,
165: * presenterChanged methods on all the dependent presenters are called
166: * (this means all dependent presenters are resolved their data).
167: * @param event the presenter event
168: */
169: protected abstract void presenterChanged(PresenterEvent event);
170:
171: /**
172: * This method should be called for notifying others that the presenter changed its data.
173: * <p/>
174: * Note: This method could be called from designChanged and presenterChanged method only.
175: */
176: protected final void firePresenterChanged() {
177: DesignComponent component = getComponent();
178: assert component != null;
179: DesignDocument document = component.getDocument();
180: assert document != null;
181: assert document.getTransactionManager().isWriteAccess();
182: assert state == State.FIRING_PRESENTER_CHANGED;
183: document.getListenerManager().firePresenterChanged(this );
184: }
185:
186: /**
187: * Adds dependency of the presenter on one that is registered on a specified component under a specified presenter class.
188: * The presenter instance is resolved dynamically. Therefore when a component changes its presenter instances,
189: * this dependency is still working.
190: * <p/>
191: * Note: This method could be called from notifyAttached method only.
192: * @param component the component
193: * @param presenterClass the presenter class
194: */
195: protected final void addDependency(DesignComponent component,
196: Class<? extends Presenter> presenterClass) {
197: assert state == State.ADDING_DEPENDENCIES;
198: if (listener == null)
199: listener = new Listener();
200: component.getDocument().getListenerManager()
201: .addPresenterListener(component, presenterClass,
202: listener);
203: }
204:
205: /**
206: * Removes dependency of the presenter on one that is registered on a specified component under a specified presenter class.
207: * <p/>
208: * Note: This method could be called from notifyDetached method only.
209: * @param component the component
210: * @param presenterClass the presenter class
211: */
212: protected final void removeDependency(DesignComponent component,
213: Class<? extends Presenter> presenterClass) {
214: assert state == State.REMOVING_DEPENDENCIES;
215: if (listener != null)
216: component.getDocument().getListenerManager()
217: .removePresenterListener(component, presenterClass,
218: listener);
219: }
220:
221: private class Listener implements DesignListener, PresenterListener {
222:
223: public void designChanged(DesignEvent event) {
224: DynamicPresenter.this .state = State.FIRING_PRESENTER_CHANGED;
225: DynamicPresenter.this .designChanged(event);
226: DynamicPresenter.this .state = State.DISABLED;
227: }
228:
229: public void presenterChanged(PresenterEvent event) {
230: DynamicPresenter.this .state = State.FIRING_PRESENTER_CHANGED;
231: DynamicPresenter.this .presenterChanged(event);
232: DynamicPresenter.this .state = State.DISABLED;
233: }
234:
235: @Override
236: public String toString() {
237: return DynamicPresenter.this .toString();
238: }
239:
240: }
241:
242: private enum State {
243:
244: DISABLED, FIRING_PRESENTER_CHANGED, ADDING_DEPENDENCIES, REMOVING_DEPENDENCIES
245:
246: }
247:
248: }
|