Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
UMLPlainTextDocument |
|
| 1.8235294117647058;1.824 |
1 | /* $Id: UMLPlainTextDocument.java 17881 2010-01-12 21:09:28Z linus $ | |
2 | ***************************************************************************** | |
3 | * Copyright (c) 2009 Contributors - see below | |
4 | * All rights reserved. This program and the accompanying materials | |
5 | * are made available under the terms of the Eclipse Public License v1.0 | |
6 | * which accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: | |
10 | * tfmorris | |
11 | ***************************************************************************** | |
12 | * | |
13 | * Some portions of this file was previously release using the BSD License: | |
14 | */ | |
15 | ||
16 | // Copyright (c) 1996-2008 The Regents of the University of California. All | |
17 | // Rights Reserved. Permission to use, copy, modify, and distribute this | |
18 | // software and its documentation without fee, and without a written | |
19 | // agreement is hereby granted, provided that the above copyright notice | |
20 | // and this paragraph appear in all copies. This software program and | |
21 | // documentation are copyrighted by The Regents of the University of | |
22 | // California. The software program and documentation are supplied "AS | |
23 | // IS", without any accompanying services from The Regents. The Regents | |
24 | // does not warrant that the operation of the program will be | |
25 | // uninterrupted or error-free. The end-user understands that the program | |
26 | // was developed for research purposes and is advised not to rely | |
27 | // exclusively on the program for any reason. IN NO EVENT SHALL THE | |
28 | // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, | |
29 | // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, | |
30 | // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF | |
31 | // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF | |
32 | // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY | |
33 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
34 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE | |
35 | // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF | |
36 | // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, | |
37 | // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
38 | ||
39 | package org.argouml.uml.ui; | |
40 | ||
41 | import java.beans.PropertyChangeEvent; | |
42 | ||
43 | import javax.swing.text.AttributeSet; | |
44 | import javax.swing.text.BadLocationException; | |
45 | import javax.swing.text.PlainDocument; | |
46 | ||
47 | import org.apache.log4j.Logger; | |
48 | import org.argouml.model.AttributeChangeEvent; | |
49 | import org.argouml.model.Model; | |
50 | import org.argouml.model.ModelEventPump; | |
51 | import org.argouml.ui.targetmanager.TargetEvent; | |
52 | import org.tigris.gef.presentation.Fig; | |
53 | ||
54 | /** | |
55 | * Model for a text property on a model element. It listens to | |
56 | * property change events for the given property name so that | |
57 | * changes made to the underlying UML model are reflected here. | |
58 | * <p> | |
59 | * NOTE: If you override the insertString() or remove() methods | |
60 | * be sure to preserve the flushEvents() calls to keep things | |
61 | * synchronized. Events caused by updates are delivered | |
62 | * asynchronously to the actual update calls. | |
63 | * <p> | |
64 | * @since Oct 6, 2002 | |
65 | * @author jaap.branderhorst@xs4all.nl | |
66 | */ | |
67 | public abstract class UMLPlainTextDocument | |
68 | extends PlainDocument | |
69 | implements UMLDocument { | |
70 | ||
71 | 900 | private static final Logger LOG = |
72 | Logger.getLogger(UMLPlainTextDocument.class); | |
73 | ||
74 | /** | |
75 | * True if an event should be fired when the text of the document is changed | |
76 | */ | |
77 | 6300 | private boolean firing = true; |
78 | ||
79 | /** | |
80 | * True if an user edits the document directly (by typing in text) | |
81 | */ | |
82 | 6300 | @Deprecated |
83 | private boolean editing = false; | |
84 | ||
85 | /** | |
86 | * The target of the propertypanel that's behind this property. | |
87 | */ | |
88 | 6300 | private Object panelTarget = null; |
89 | ||
90 | /** | |
91 | * The name of the property set event that will change the | |
92 | * property this document shows. | |
93 | */ | |
94 | 6300 | private String eventName = null; |
95 | ||
96 | /** | |
97 | * Constructor for UMLPlainTextDocument. This takes a panel to set the | |
98 | * thirdpartyeventlistener to the given list of events to listen to. | |
99 | * | |
100 | * @param name the event | |
101 | */ | |
102 | public UMLPlainTextDocument(String name) { | |
103 | 6300 | super(); |
104 | 6300 | setEventName(name); |
105 | 6300 | } |
106 | ||
107 | /* | |
108 | * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) | |
109 | */ | |
110 | public void propertyChange(PropertyChangeEvent evt) { | |
111 | // NOTE: This may be called from a different thread, so we need to be | |
112 | // careful of the threading restrictions imposed by AbstractDocument | |
113 | // for mutators to be sure we don't deadlock. | |
114 | 0 | if (evt instanceof AttributeChangeEvent |
115 | && eventName.equals(evt.getPropertyName())) { | |
116 | 0 | updateText((String) evt.getNewValue()); |
117 | } | |
118 | 0 | } |
119 | ||
120 | /** | |
121 | * Returns the target. | |
122 | * @return Object | |
123 | */ | |
124 | public final Object getTarget() { | |
125 | 0 | return panelTarget; |
126 | } | |
127 | ||
128 | /** | |
129 | * Sets the target. | |
130 | * @param target The target to set | |
131 | */ | |
132 | public final void setTarget(Object target) { | |
133 | 0 | target = target instanceof Fig ? ((Fig) target).getOwner() : target; |
134 | 0 | if (Model.getFacade().isAUMLElement(target)) { |
135 | 0 | if (target != panelTarget) { |
136 | 0 | ModelEventPump eventPump = Model.getPump(); |
137 | 0 | if (panelTarget != null) { |
138 | 0 | eventPump.removeModelEventListener(this, panelTarget, |
139 | getEventName()); | |
140 | } | |
141 | 0 | panelTarget = target; |
142 | 0 | eventPump.addModelEventListener(this, panelTarget, |
143 | getEventName()); | |
144 | } | |
145 | 0 | updateText(getProperty()); |
146 | } | |
147 | 0 | } |
148 | ||
149 | /* | |
150 | * @see javax.swing.text.Document#insertString( | |
151 | * int, java.lang.String, javax.swing.text.AttributeSet) | |
152 | */ | |
153 | public void insertString(int offset, String str, AttributeSet a) | |
154 | throws BadLocationException { | |
155 | ||
156 | // Mutators hold write lock & will deadlock if use is not thread safe | |
157 | 0 | super.insertString(offset, str, a); |
158 | ||
159 | 0 | setPropertyInternal(getText(0, getLength())); |
160 | 0 | } |
161 | ||
162 | ||
163 | /* | |
164 | * @see javax.swing.text.Document#remove(int, int) | |
165 | */ | |
166 | public void remove(int offs, int len) throws BadLocationException { | |
167 | ||
168 | // Mutators hold write lock & will deadlock if use is not thread safe | |
169 | 0 | super.remove(offs, len); |
170 | ||
171 | 0 | setPropertyInternal(getText(0, getLength())); |
172 | 0 | } |
173 | ||
174 | /** | |
175 | * Wrapped version of setProperty which attempts to keep us from hearing | |
176 | * our own echo on the event listener when we change something. Also | |
177 | * skips updates equal the current value. | |
178 | */ | |
179 | private void setPropertyInternal(String newValue) { | |
180 | // TODO: This is updating model on a per character basis as | |
181 | // well as unregistering/reregistering event listeners every | |
182 | // character - very wasteful - tfm | |
183 | 0 | if (isFiring() && !newValue.equals(getProperty())) { |
184 | 0 | setFiring(false); |
185 | 0 | setProperty(newValue); |
186 | 0 | Model.getPump().flushModelEvents(); |
187 | 0 | setFiring(true); |
188 | } | |
189 | 0 | } |
190 | ||
191 | /** | |
192 | * @param text the value of the property | |
193 | */ | |
194 | protected abstract void setProperty(String text); | |
195 | ||
196 | /** | |
197 | * @return the value of the property | |
198 | */ | |
199 | protected abstract String getProperty(); | |
200 | ||
201 | /** | |
202 | * Enable/disable firing of updates. As a side effect, it unregisters | |
203 | * model event listeners during disable and registers them again during | |
204 | * enable. | |
205 | * | |
206 | * @param f new firing state. Pass false to disable updates. | |
207 | */ | |
208 | private final synchronized void setFiring(boolean f) { | |
209 | 0 | ModelEventPump eventPump = Model.getPump(); |
210 | 0 | if (f && panelTarget != null) { |
211 | 0 | eventPump.addModelEventListener(this, panelTarget, eventName); |
212 | } else { | |
213 | 0 | eventPump.removeModelEventListener(this, panelTarget, eventName); |
214 | } | |
215 | 0 | firing = f; |
216 | 0 | } |
217 | ||
218 | /** | |
219 | * Return the state of the firing flag. Method is synchronized so it will | |
220 | * return the correct value from any thread. | |
221 | * | |
222 | * @return true firing of updates is allowed currently. Returns false if we | |
223 | * are in the process of doing an update so that we know to ignore | |
224 | * any resulting events. | |
225 | */ | |
226 | private final synchronized boolean isFiring() { | |
227 | 0 | return firing; |
228 | } | |
229 | ||
230 | private final void updateText(String textValue) { | |
231 | try { | |
232 | 0 | if (textValue == null) { |
233 | 0 | textValue = ""; |
234 | } | |
235 | 0 | String currentValue = getText(0, getLength()); |
236 | 0 | if (isFiring() && !textValue.equals(currentValue)) { |
237 | 0 | setFiring(false); |
238 | ||
239 | // Mutators hold write lock & will deadlock | |
240 | // if use is not thread-safe | |
241 | 0 | super.remove(0, getLength()); |
242 | 0 | super.insertString(0, textValue, null); |
243 | } | |
244 | 0 | } catch (BadLocationException b) { |
245 | 0 | LOG.error( |
246 | "A BadLocationException happened\n" | |
247 | + "The string to set was: " | |
248 | + getProperty(), | |
249 | b); | |
250 | } finally { | |
251 | 0 | setFiring(true); |
252 | 0 | } |
253 | 0 | } |
254 | ||
255 | ||
256 | /** | |
257 | * Returns the eventName. | |
258 | * @return String | |
259 | */ | |
260 | public String getEventName() { | |
261 | 0 | return eventName; |
262 | } | |
263 | ||
264 | /** | |
265 | * Sets the eventName. | |
266 | * @param en The eventName to set | |
267 | */ | |
268 | protected void setEventName(String en) { | |
269 | 6300 | eventName = en; |
270 | 6300 | } |
271 | ||
272 | /* | |
273 | * @see org.argouml.ui.targetmanager.TargetListener#targetAdded(org.argouml.ui.targetmanager.TargetEvent) | |
274 | */ | |
275 | public void targetAdded(TargetEvent e) { | |
276 | 0 | setTarget(e.getNewTarget()); |
277 | 0 | } |
278 | ||
279 | /* | |
280 | * @see org.argouml.ui.targetmanager.TargetListener#targetRemoved(org.argouml.ui.targetmanager.TargetEvent) | |
281 | */ | |
282 | public void targetRemoved(TargetEvent e) { | |
283 | 0 | setTarget(e.getNewTarget()); |
284 | 0 | } |
285 | ||
286 | /* | |
287 | * @see org.argouml.ui.targetmanager.TargetListener#targetSet(org.argouml.ui.targetmanager.TargetEvent) | |
288 | */ | |
289 | public void targetSet(TargetEvent e) { | |
290 | 0 | setTarget(e.getNewTarget()); |
291 | 0 | } |
292 | ||
293 | } |