Source code: org/merlotxml/merlot/GenericDOMEditor.java
1 /*
2 ====================================================================
3 Copyright (c) 1999-2000 ChannelPoint, Inc.. All rights reserved.
4 ====================================================================
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 1. Redistribution of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12
13 2. Redistribution in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16
17 3. All advertising materials mentioning features or use of this
18 software must display the following acknowledgment: "This product
19 includes software developed by ChannelPoint, Inc. for use in the
20 Merlot XML Editor (http://www.merlotxml.org/)."
21
22 4. Any names trademarked by ChannelPoint, Inc. must not be used to
23 endorse or promote products derived from this software without prior
24 written permission. For written permission, please contact
25 legal@channelpoint.com.
26
27 5. Products derived from this software may not be called "Merlot"
28 nor may "Merlot" appear in their names without prior written
29 permission of ChannelPoint, Inc.
30
31 6. Redistribution of any form whatsoever must retain the following
32 acknowledgment: "This product includes software developed by
33 ChannelPoint, Inc. for use in the Merlot XML Editor
34 (http://www.merlotxml.org/)."
35
36 THIS SOFTWARE IS PROVIDED BY CHANNELPOINT, INC. "AS IS" AND ANY EXPRESSED OR
37 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
38 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
39 EVENT SHALL CHANNELPOINT, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
40 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
41 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
45 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 ====================================================================
47
48 For more information on ChannelPoint, Inc. please see http://www.channelpoint.com.
49 For information on the Merlot project, please see
50 http://www.merlotxml.org
51 */
52
53 package org.merlotxml.merlot;
54
55 import java.io.*;
56 import java.awt.event.*;
57 import java.beans.*;
58
59 import java.util.*;
60 import javax.swing.*;
61 import javax.swing.tree.*;
62 import java.awt.dnd.*;
63 import java.awt.datatransfer.*;
64 import java.io.*;
65 import org.w3c.dom.*;
66 import java.text.*;
67 import java.awt.Dimension;
68 import java.awt.Rectangle;
69 import matthew.awt.StrutLayout;
70 import org.merlotxml.util.xml.*;
71
72
73 /**
74 * Editor of nodes and such
75 *
76 */
77
78 public class GenericDOMEditor implements MerlotDOMEditor, MerlotConstants
79 {
80 private static final String[] _editableTypes = {"ALL"};
81
82 protected static Hashtable _invalidCharHash;
83 protected static Hashtable _manditoryFieldHash;
84
85 public static final String SANITY_CHARS_PROP = "merlot.editor.generic.form.invalidchars";
86 public static final String SANITY_MANDITORY_PROP = "merlot.editor.generic.form.manditoryfields";
87
88
89
90 public GenericDOMEditor ()
91 {
92
93
94 }
95 /**
96 * Returns a set of menu items for any special actions for
97 * this particular editor
98 * that it wants in the popup menu. Standard stuff like cut, copy, paste
99 * is taken care of by other objects. If nothing needs added
100 */
101 public JMenuItem[] getMenuItems(MerlotDOMNode node)
102 {
103 return null;
104 }
105
106
107
108 /**
109 * returns a panel for editing this type of component.
110 */
111 public JPanel getEditPanel(MerlotDOMNode node)
112 {
113 JPanel p = null;
114
115 Node nd = node.getRealNode();
116
117 int t = nd.getNodeType();
118 switch (t) {
119 case Node.TEXT_NODE:
120 p = new TextEditPanel((MerlotDOMText)node);
121 break;
122 case Node.COMMENT_NODE:
123 p = new CommentEditPanel((MerlotDOMComment)node);
124 break;
125 case Node.PROCESSING_INSTRUCTION_NODE:
126 p = new ProcInstructionEditPanel((MerlotDOMProcessingInstruction)node);
127 break;
128 case Node.DOCUMENT_NODE:
129 case Node.DOCUMENT_TYPE_NODE:
130 case Node.DOCUMENT_FRAGMENT_NODE:
131 default:
132 p = new GenericDOMEditPanel(node);
133 break;
134 }
135 if (p != null)
136 installListener(p,node);
137
138 return p;
139
140 }
141
142 public void savePanel(JPanel p)
143 throws PropertyVetoException
144 {
145 if (p instanceof GenericDOMEditPanel) {
146 ((GenericDOMEditPanel)p).save();
147 }
148 else {
149 MerlotDebug.msg("Cannot save! Panel is not the right type.");
150 }
151
152
153 }
154
155
156 /**
157 * Returns the element types that this editor handles
158 */
159 public String[] getEditableTypes()
160 {
161 return _editableTypes;
162 }
163
164
165
166 /**
167 * returns true if this editor also edits it's children. If an editor
168 * says it handles it's children, then it must handle all it's children.<P>
169 */
170 public boolean editsChildren()
171 {
172 return false;
173 }
174
175 public void grabFocus(JPanel p)
176 {
177 if (p instanceof GenericDOMEditPanel) {
178 ((GenericDOMEditPanel)p).grabFocus();
179 }
180
181
182 }
183
184 /**
185 * Returns true if the component editor wants a particular node hidden
186 * from the user. If the editor wants to filter
187 * what the user sees in their display, it should look at the
188 * given node, otherwise it should return false
189 */
190 public boolean suppressNode(MerlotDOMNode node)
191 {
192 // check this node out to see if it's a #text or #comment
193 // node, if so, hide it
194
195 Node nd = node.getRealNode();
196 // MerlotDebug.msg("filter nd = "+nd+" nodename = "+node.getNodeName());
197
198 int t = nd.getNodeType();
199 switch (t) {
200 case Node.TEXT_NODE:
201
202 if (nd instanceof CharacterData) {
203
204
205 // MerlotDebug.msg("#text = "+s);
206 if (node instanceof MerlotDOMText && ((MerlotDOMText)node).isVisible()) {
207 return false;
208 }
209 String s = ((CharacterData)nd).getData();
210 if (s != null) {
211 s = s.trim();
212 if ( s.equals("")) {
213 return true;
214 }
215 return false;
216 }
217 }
218
219 return true;
220 case Node.COMMENT_NODE:
221 return false;
222 case Node.PROCESSING_INSTRUCTION_NODE:
223 return false;
224 case Node.DOCUMENT_NODE:
225 return true;
226 case Node.DOCUMENT_TYPE_NODE:
227 return true;
228 case Node.DOCUMENT_FRAGMENT_NODE:
229 return true;
230
231 }
232
233
234 return false;
235 }
236
237 public boolean suppressAddType(DTDElement el)
238 {
239 return false;
240 }
241
242 public void installListener(JPanel p, MerlotDOMNode node)
243 {
244 if (p instanceof GenericDOMEditPanel) {
245 // install us as a listener
246 MerlotDebug.msg("installing listener for "+p);
247
248 ((GenericDOMEditPanel)p).addVetoableChangeListener(new GenericSanityCheckListener());
249 }
250 }
251
252 protected static final int START=0;
253 protected static final int NODENAME=1;
254 protected static final int ATTRNAME=2;
255 protected static final int EQUALS=3;
256
257 protected static void parseInvalidCharsProp(String prop)
258 {
259 int tokenstate = START;
260 Vector names = null;
261 Vector fields = null;
262 StringBuffer chars = null;
263 int err = 0;
264 if (prop == null) {
265 return;
266 }
267
268 StringTokenizer tok = new StringTokenizer(prop,"=., ",true);
269 while (tok.hasMoreTokens()) {
270 String s = tok.nextToken();
271 if (s.length() > 0) {
272 char c = s.charAt(0);
273 switch (tokenstate) {
274 case START:
275 if (names != null && fields != null && chars != null) {
276 for (int i=0;i<names.size(); i++) {
277 for (int j=0;j<fields.size();j++) {
278 String key = (String)names.elementAt(i) + "."+ (String)fields.elementAt(j);
279 MerlotDebug.msg("GenericDOMEditor: putting key="+key+" chars='"+chars.toString()+"'");
280 _invalidCharHash.put(key,chars.toString());
281 }
282 }
283 names = null;
284 fields = null;
285 chars = null;
286 }
287 if (tok.hasMoreTokens()) {
288 names = new Vector();
289 fields = new Vector();
290 }
291
292 switch (c) {
293 case ',':
294 case '=':
295 case '.':
296 case ' ':
297 err++;
298 break;
299 default:
300 names.add(s);
301 tokenstate=NODENAME;
302 break;
303 }
304 break;
305 case NODENAME:
306 switch (c) {
307 case '=':
308 err++;
309 break;
310 case ',':
311 break;
312 case '.':
313 tokenstate=ATTRNAME;
314 break;
315 case ' ':
316 err++;
317 break;
318 default:
319 names.add(s);
320 break;
321 }
322 break;
323 case ATTRNAME:
324 switch (c) {
325 case '=':
326 chars = new StringBuffer();
327 tokenstate=EQUALS;
328 break;
329 case ',':
330 break;
331 case '.':
332 case ' ':
333 err++;
334 break;
335 default:
336 fields.add(s);
337 break;
338 }
339 break;
340 case EQUALS:
341 switch (c) {
342 case ' ':
343 tokenstate=START;
344 break;
345 default:
346 for (int i=0;i<s.length();i++) {
347 if (s.charAt(i) == '{') {
348 int x = s.indexOf('}');
349 if (x > 0) {
350 String word = s.substring(i+1,x);
351 if (word.equals("space")) {
352 chars.append(' ');
353 }
354 i+=x;
355 continue;
356 }
357 }
358 chars.append(s.charAt(i));
359 }
360 }
361 break;
362 }
363 }
364 }
365 if (names != null && fields != null && chars != null) {
366 for (int i=0;i<names.size(); i++) {
367 for (int j=0;j<fields.size();j++) {
368 String key = (String)names.elementAt(i) + "." + (String)fields.elementAt(j);
369 MerlotDebug.msg("GenericDOMEditor: putting key="+key+" chars='"+chars.toString()+"'");
370 _invalidCharHash.put(key,chars.toString());
371 }
372 }
373 names = null;
374 fields = null;
375 chars = null;
376 }
377
378 }
379
380 protected static void parseManditoryFieldsProp(String prop)
381 {
382 int tokenstate = START;
383 Vector names = null;
384 Vector fields = null;
385 int err = 0;
386 if (prop == null) {
387 return;
388 }
389
390 StringTokenizer tok = new StringTokenizer(prop,"=, ",true);
391 while (tok.hasMoreTokens()) {
392 String s = tok.nextToken();
393 if (s.length() > 0) {
394 char c = s.charAt(0);
395 switch (tokenstate) {
396 case START:
397 if (names != null && fields != null) {
398 for (int i=0;i<names.size(); i++) {
399 for (int j=0;j<fields.size();j++) {
400 String key = (String)names.elementAt(i) + "."+ (String)fields.elementAt(j);
401 MerlotDebug.msg("GenericDOMEditor: putting manditory field key="+key);
402 _manditoryFieldHash.put(key,"yes");
403 }
404 }
405 names = null;
406 fields = null;
407 }
408 if (tok.hasMoreTokens()) {
409 names = new Vector();
410 fields = new Vector();
411 }
412 switch (c) {
413 case ',':
414 case '=':
415 case '.':
416 case ' ':
417 err++;
418 break;
419 default:
420 names.add(s);
421 tokenstate=NODENAME;
422 break;
423 }
424 break;
425 case NODENAME:
426 switch (c) {
427 case '=':
428 tokenstate=EQUALS;
429 break;
430 case ',':
431 break;
432 case '.':
433 tokenstate=ATTRNAME;
434 break;
435 case ' ':
436 err++;
437 break;
438 default:
439 names.add(s);
440 break;
441 }
442 break;
443 case ATTRNAME:
444 switch (c) {
445 case '=':
446 tokenstate=EQUALS;
447 break;
448 case ',':
449 break;
450 case '.':
451 case ' ':
452 err++;
453 break;
454 default:
455 fields.add(s);
456 break;
457 }
458 break;
459 case EQUALS:
460 switch (c) {
461 case ' ':
462 tokenstate=START;
463 break;
464 case ',':
465 break;
466
467 default:
468 fields.add(s);
469 break;
470 }
471 break;
472 }
473 }
474 }
475 if (names != null && fields != null) {
476 for (int i=0;i<names.size(); i++) {
477 for (int j=0;j<fields.size();j++) {
478 String key = (String)names.elementAt(i) + "."+ (String)fields.elementAt(j);
479 MerlotDebug.msg("GenericDOMEditor: putting manditory field key="+key);
480 _manditoryFieldHash.put(key,"yes");
481 }
482 }
483 names = null;
484 fields = null;
485 }
486
487 }
488
489
490 protected static String getInvalidChars(MerlotDOMNode node, String fieldname)
491 {
492 if (_invalidCharHash == null) {
493 _invalidCharHash = new Hashtable();
494 // load this up from the properties
495 String invalidcharsprop = XMLEditorSettings.getSharedInstance().getProperty(SANITY_CHARS_PROP);
496 parseInvalidCharsProp(invalidcharsprop);
497
498
499 }
500 if (node != null) {
501 String nodename = node.getNodeName();
502 return (String)_invalidCharHash.get(nodename+"."+fieldname);
503 }
504 return null;
505
506 }
507
508 protected static String getManditoryFields(MerlotDOMNode node, String fieldname)
509 {
510 if (_manditoryFieldHash == null) {
511 _manditoryFieldHash = new Hashtable();
512 String manditoryfields = XMLEditorSettings.getSharedInstance().getProperty(SANITY_MANDITORY_PROP);
513 parseManditoryFieldsProp(manditoryfields);
514
515 }
516 if (node != null) {
517 String nodename = node.getNodeName();
518 return (String)_manditoryFieldHash.get(nodename+"."+fieldname);
519 }
520 return null;
521
522 }
523
524
525 public static class GenericSanityCheckListener implements VetoableChangeListener
526 {
527
528 public GenericSanityCheckListener()
529 {
530 }
531
532
533 public void vetoableChange(PropertyChangeEvent evt)
534 throws PropertyVetoException
535 {
536 // System.out.println("vetoableChange");
537
538
539 // if the name has spaces or is blank, throw an exception
540 String t;
541 Object n = evt.getSource();
542 if (!(n instanceof MerlotDOMNode)) {
543 return;
544 }
545 MerlotDOMNode node = (MerlotDOMNode)n;
546
547 String s = evt.getPropertyName();
548 Object o = evt.getNewValue();
549 if (o instanceof String) {
550 t = ((String)o).trim();
551 }
552 else {
553 t = null;
554 }
555
556 String invalidchars = GenericDOMEditor.getInvalidChars(node,s);
557 if (t != null && invalidchars != null) {
558
559 // System.out.println("invalidchars = "+invalidchars);
560 // check the field value for invalid chars
561 for (int i=0;i<invalidchars.length();i++) {
562 char c = invalidchars.charAt(i);
563 if (t.indexOf(c) >= 0) {
564 String[] err = new String[3];
565 err[0] = node.getNodeName();
566 err[1] = s;
567 switch (c) {
568 case ' ':
569 err[2] = "space";
570 break;
571 case '.':
572 err[2] = "period";
573 break;
574 case ',':
575 err[2] = "comma";
576 break;
577 default:
578 err[2] = "'" + c + "'";
579 break;
580
581 }
582
583 throw new PropertyVetoException(MessageFormat.format(MerlotResource.getString(ERR,"illegal.value.attr.char"),err),evt);
584 }
585
586 }
587 }
588
589 String manditory = GenericDOMEditor.getManditoryFields(node,s);
590 if (manditory != null && manditory.equals("yes")) {
591 if (t == null || t.equals("")) {
592 String err[] = new String[2];
593 err[0] = node.getNodeName();
594 err[1] = s;
595
596 throw new PropertyVetoException(MessageFormat.format(MerlotResource.getString(ERR,"manditory.field"),err),evt);
597 }
598 }
599 }
600 }
601
602
603
604
605 }