Source code: com/virtuosotechnologies/asaph/standardmodel/StdChordAnnotation.java
1 /*
2 ================================================================================
3
4 FILE: StdChordAnnotation.java
5
6 PROJECT:
7
8 Asaph
9
10 CONTENTS:
11
12 Standard implementation of ChordAnnotation
13
14 PROGRAMMERS:
15
16 Daniel Azuma (DA) <dazuma@kagi.com>
17
18 COPYRIGHT:
19
20 Copyright (C) 2003 Daniel Azuma (dazuma@kagi.com)
21
22 This program is free software; you can redistribute it and/or
23 modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2
25 of the License, or (at your option) any later version.
26
27 This program is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public
33 License along with this program; if not, write to
34 Free Software Foundation, Inc.
35 59 Temple Place, Suite 330
36 Boston, MA 02111-1307 USA
37
38 ================================================================================
39 */
40
41
42 package com.virtuosotechnologies.asaph.standardmodel;
43
44
45 import java.io.IOException;
46 import javax.swing.undo.AbstractUndoableEdit;
47 import javax.swing.undo.CannotUndoException;
48 import javax.swing.undo.CannotRedoException;
49 import javax.swing.event.UndoableEditListener;
50 import org.xml.sax.ErrorHandler;
51 import org.xml.sax.Locator;
52
53 import com.virtuosotechnologies.lib.xml.XMLUnparser;
54
55 import com.virtuosotechnologies.asaph.model.ChordAnnotation;
56 import com.virtuosotechnologies.asaph.model.SongLine;
57 import com.virtuosotechnologies.asaph.model.ChordSet;
58 import com.virtuosotechnologies.asaph.model.notation.Chord;
59
60
61 /**
62 * Standard implementation of ChordAnnotation
63 */
64 /*package*/ class StdChordAnnotation
65 extends BaseSongMember
66 implements
67 ChordAnnotation
68 {
69 private StdChordSet chordSet_;
70 private Chord main_;
71 private Chord[] pre_;
72 private Chord[] post_;
73
74
75 /*package*/ StdChordAnnotation(
76 StdSongLine parent,
77 StdChordSet chordSet,
78 Chord main,
79 Chord[] pre,
80 Chord[] post)
81 {
82 super(parent);
83 assert parent.getSongBlock().getSong() == chordSet.getSong();
84 if (main == null)
85 {
86 throw new NullPointerException();
87 }
88 chordSet_ = chordSet;
89 main_ = main;
90 if (pre == null)
91 {
92 pre_ = new Chord[0];
93 }
94 else
95 {
96 pre_ = new Chord[pre.length];
97 System.arraycopy(pre, 0, pre_, 0, pre.length);
98 }
99 if (post == null)
100 {
101 post_ = new Chord[0];
102 }
103 else
104 {
105 post_ = new Chord[post.length];
106 System.arraycopy(post, 0, post_, 0, post.length);
107 }
108 }
109
110
111 /*package*/ void unparse(
112 XMLUnparser unparser)
113 throws
114 IOException
115 {
116 unparser.startSingleLineElement(XMLConstants.ANNOTATION_ELEMENT);
117 if (chordSet_ != getSong().getDefaultChordSet())
118 {
119 unparser.addAttribute(XMLConstants.ANNOTATION_CHORDSET_ATTRIBUTE,
120 chordSet_.getSerializableID());
121 }
122 if (pre_.length > 0)
123 {
124 unparser.addAttribute(XMLConstants.ANNOTATION_PRECEDING_ATTRIBUTE,
125 pre_[0].getNotationFactory().unparseChordArray(pre_));
126 }
127 if (!main_.isEmpty())
128 {
129 unparser.addAttribute(XMLConstants.ANNOTATION_PRIMARY_ATTRIBUTE,
130 main_.generateString());
131 }
132 if (post_.length > 0)
133 {
134 unparser.addAttribute(XMLConstants.ANNOTATION_FOLLOWING_ATTRIBUTE,
135 post_[0].getNotationFactory().unparseChordArray(post_));
136 }
137 unparser.endElement(XMLConstants.ANNOTATION_ELEMENT);
138 }
139
140
141 /*package*/ class ParseHandler
142 extends ParseHandlerBase
143 {
144 /*package*/ ParseHandler(
145 ErrorHandler errorHandler,
146 Locator locator)
147 {
148 super(errorHandler, locator, XMLConstants.ANNOTATION_ELEMENT);
149 }
150 }
151
152
153 //-------------------------------------------------------------------------
154 // Methods of SongLineMember
155 //-------------------------------------------------------------------------
156
157 /**
158 * Get the containing song line
159 *
160 * @return SongLine containing this member
161 */
162 public SongLine getSongLine()
163 {
164 return (SongLine)internalGetParent();
165 }
166
167
168 //-------------------------------------------------------------------------
169 // Methods of ChordAnnotation
170 //-------------------------------------------------------------------------
171
172 /**
173 * Get the ChordSet relevant to this annotation. Every annotation
174 * is part of a ChordSet. Note that this method may be slow, since
175 * implementations may need to search the Song for the correct
176 * ChordSet. If all you need to do is determine whether this
177 * annotation is associated with a given ChordSet, use isInChordSet
178 * instead, which may be optimized by the implementation.
179 *
180 * @return ChordSet this annotation is part of
181 */
182 public ChordSet getChordSet()
183 {
184 return chordSet_;
185 }
186
187
188 /**
189 * Returns true if this annotation in the given ChordSet.
190 * Implementations of this method may be faster than
191 * getChordSet().equals(). Returns false but does not throw
192 * an exception if the given ChordSet is null, or is not a
193 * member of the Song containing this annotation
194 *
195 * @param cs ChordSet to match with
196 * @return true if this annotation is in the given ChordSet
197 */
198 public boolean isInChordSet(
199 ChordSet cs)
200 {
201 return chordSet_ == cs;
202 }
203
204
205 /**
206 * Get the primary chord.
207 *
208 * @return the chord
209 */
210 public Chord getPrimaryChord()
211 {
212 return main_;
213 }
214
215
216 /**
217 * Get the array of chords that should precede the main chord.
218 * This will return an empty array if there are no preceding chords.
219 *
220 * @return the chord array
221 */
222 public Chord[] getPrecedingChords()
223 {
224 Chord[] ret = new Chord[pre_.length];
225 System.arraycopy(pre_, 0, ret, 0, pre_.length);
226 return ret;
227 }
228
229
230 /**
231 * Get the array of chords that should follow the main chord.
232 * This will return an empty array if there are no following chords.
233 *
234 * @return the chord array
235 */
236 public Chord[] getFollowingChords()
237 {
238 Chord[] ret = new Chord[post_.length];
239 System.arraycopy(post_, 0, ret, 0, post_.length);
240 return ret;
241 }
242
243
244 /**
245 * Set the primary chord. The primary chord may not be null.
246 *
247 * @param chord the chord.
248 * @param undoListener listener to notify if an undoable edit is generated,
249 * or null to suppress generation of undoable edits
250 */
251 public void setPrimaryChord(
252 final Chord chord,
253 UndoableEditListener undoListener)
254 {
255 if (chord == null)
256 {
257 throw new NullPointerException();
258 }
259 if (chord.equals(main_))
260 {
261 return;
262 }
263 final Chord oldChord = main_;
264 main_ = chord;
265 if (undoListener != null)
266 {
267 internalReportUndoableEdit(undoListener,
268 new AbstractUndoableEdit()
269 {
270 public void undo()
271 throws CannotUndoException
272 {
273 super.undo();
274 main_ = oldChord;
275 }
276
277 public void redo()
278 throws CannotRedoException
279 {
280 super.redo();
281 main_ = chord;
282 }
283 });
284 }
285 }
286
287
288 private Chord[] analyzeNewChords(
289 Chord[] chords,
290 Chord[] oldChords)
291 {
292 final Chord[] newChords;
293 if (chords == null)
294 {
295 newChords = new Chord[0];
296 }
297 else
298 {
299 newChords = new Chord[chords.length];
300 System.arraycopy(chords, 0, newChords, 0, chords.length);
301 }
302 if (newChords.length != oldChords.length)
303 {
304 return newChords;
305 }
306 for (int i=0; i<newChords.length; ++i)
307 {
308 if (!newChords[i].equals(oldChords[i]))
309 {
310 return newChords;
311 }
312 }
313 return null;
314 }
315
316
317 /**
318 * Set the array of chords that should precede the main one.
319 * You may pass null or the empty array for no preceding chords.
320 *
321 * @param chords the chord array
322 * @param undoListener listener to notify if an undoable edit is generated,
323 * or null to suppress generation of undoable edits
324 */
325 public void setPrecedingChords(
326 Chord[] chords,
327 UndoableEditListener undoListener)
328 {
329 final Chord[] newChords = analyzeNewChords(chords, pre_);
330 if (newChords == null)
331 {
332 return;
333 }
334 final Chord[] oldChords = pre_;
335 pre_ = newChords;
336 if (undoListener != null)
337 {
338 internalReportUndoableEdit(undoListener,
339 new AbstractUndoableEdit()
340 {
341 public void undo()
342 throws CannotUndoException
343 {
344 super.undo();
345 pre_ = oldChords;
346 }
347
348 public void redo()
349 throws CannotRedoException
350 {
351 super.redo();
352 pre_ = newChords;
353 }
354 });
355 }
356 }
357
358
359 /**
360 * Set the array of chords that should follow the main one.
361 * You may pass null or the empty array for no following chords.
362 *
363 * @param chords the chord array
364 * @param undoListener listener to notify if an undoable edit is generated,
365 * or null to suppress generation of undoable edits
366 */
367 public void setFollowingChords(
368 Chord[] chords,
369 UndoableEditListener undoListener)
370 {
371 final Chord[] newChords = analyzeNewChords(chords, post_);
372 if (newChords == null)
373 {
374 return;
375 }
376 final Chord[] oldChords = post_;
377 post_ = newChords;
378 if (undoListener != null)
379 {
380 internalReportUndoableEdit(undoListener,
381 new AbstractUndoableEdit()
382 {
383 public void undo()
384 throws CannotUndoException
385 {
386 super.undo();
387 post_ = oldChords;
388 }
389
390 public void redo()
391 throws CannotRedoException
392 {
393 super.redo();
394 post_ = newChords;
395 }
396 });
397 }
398 }
399 }