Source code: org/merlotxml/merlot/GenericDOMEditPanel.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.channelpoint.com/merlot/)."
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.channelpoint.com/merlot/)."
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.channelpoint.com/merlot.
51 */
52
53
54 // Copyright 1999 ChannelPoint, Inc., All Rights Reserved.
55
56 package org.merlotxml.merlot;
57
58 import java.io.*;
59 import java.awt.*;
60 import java.beans.*;
61 import java.text.MessageFormat;
62
63 import javax.swing.border.*;
64 import java.awt.event.*;
65 import java.util.*;
66 import org.w3c.dom.*;
67 import javax.swing.*;
68 import javax.swing.text.*;
69 import com.sun.javax.swing.*;
70
71 import matthew.awt.StrutLayout;
72 import com.ibm.xml.parser.*;
73 import org.merlotxml.util.xml.*;
74
75
76 /**
77 * This is a generic node editing panel which provides a component for each attribute
78 * listed with the element it's created to edit, along with a text box for PCDATA.
79 * <P>
80 * This class can be extended to change what the user sees for each attribute field.
81 * Typically the easiest methods to overload for this type of custom editors are getEditComponent()
82 * and sometimes save().
83 *
84 * @author Kelly Campbell
85 */
86
87 public class GenericDOMEditPanel extends JPanel implements MerlotConstants
88 {
89
90 private static final String HIDE_PROP = "merlot.editor.generic.hide";
91 private static final String RO_PROP = "merlot.editor.generic.readonly";
92
93 public static final int ALIGN_TOP = 0;
94 public static final int ALIGN_MIDDLE = 1;
95 public static final int ALIGN_BOTTOM = 2;
96
97 /**
98 * The icon to use for required attribute labels
99 */
100 protected static Icon _requiredAttrIcon = null;
101
102 /**
103 * The node this editor was created for
104 */
105 protected MerlotDOMNode _node;
106 /**
107 * A node which is the child #text element for this node
108 */
109 protected MerlotDOMText _subtext;
110
111 /**
112 * The attributes and their values from this node
113 */
114 protected NamedNodeMap _node_attributes;
115
116 /**
117 * Map of attribute names to their DTDAttribute declaration
118 */
119 protected Hashtable _dtd_attributes;
120
121 /**
122 * Map of attribute names to attribute components (key is String, val is JComponent)
123 */
124 protected Hashtable _attrComponents;
125
126 /*=$**/
127 protected boolean _first_component = true;
128 protected JComponent _prev = null;
129 /*=$**/
130 protected JComponent _first_field = null;
131
132 protected JTextArea _text = null;
133 protected JTextField _target = null;
134
135 /**
136 * A list of attributes that should be hidden. Key is element.attr. If value is nonnull,
137 * then the attribute is hidden in the generic panel. #text can also be hidden if key
138 * '#text' is in this hash
139 */
140 private static Hashtable _hideAttrs;
141
142 /**
143 * Similar to the hidable attributes, except these are displayed, but can't be changed
144 */
145 /*=$**/
146 protected static Hashtable _readonlyAttrs;
147
148 /**
149 * list of PropertyChangeListeners that can veto editing actions
150 */
151 /*=$*/
152 protected Vector _vetoListeners;
153 /*=$*/
154
155 /**
156 * The panel which contains the actual layout of attributes
157 */
158 /*=$**/
159 protected JPanel _attributePanel;
160
161
162 public GenericDOMEditPanel()
163 {
164 super();
165 }
166
167
168 public GenericDOMEditPanel(MerlotDOMNode node)
169 {
170 super();
171 _node = node;
172
173 buildPanel();
174 }
175
176 protected void buildPanel()
177 {
178 init();
179 setupReadonlyTable();
180 setupHideTable();
181
182 _vetoListeners = new Vector();
183
184 initPanelLayout();
185 setupPanel();
186 addVetoableChangeListener(new StandardAttributeChecker());
187 }
188
189
190 protected void init()
191 {
192 _node_attributes = _node.getAttributes();
193 _dtd_attributes = new Hashtable();
194 _attrComponents = new Hashtable();
195 }
196
197 protected void initPanelLayout()
198 {
199 _attributePanel = new JPanel();
200 _attributePanel.setMinimumSize(new Dimension(4,4));
201
202 StrutLayout slay = new StrutLayout();
203 slay.setDefaultStrutLength(10);
204
205 _attributePanel.setLayout(slay);
206
207 // get the root element for the strut layout.. it will be the icon of the node
208 JLabel iconLabel = new JLabel(_node.getIcon());
209 _prev = iconLabel;
210 _attributePanel.add(iconLabel);
211
212 // wrap a ScrollPane around it
213 ScrollablePanel scrollPanel = new ScrollablePanel(true,false);
214 StrutLayout lay = new StrutLayout();
215 scrollPanel.setLayout(lay);
216 scrollPanel.add(_attributePanel);
217 lay.setSprings(_attributePanel, StrutLayout.SPRING_BOTH);
218 scrollPanel.setMinimumSize(new Dimension(4,4));
219 scrollPanel.setBorder(new EmptyBorder(0,0,0,5));
220
221 JScrollPane sp = new JScrollPane(scrollPanel,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
222 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
223
224 sp.setMinimumSize(new Dimension(4,4));
225 sp.getViewport().setMinimumSize(new Dimension(4,4));
226
227 lay = new StrutLayout();
228 this.setLayout(lay);
229 this.add(sp);
230 lay.setSprings(sp, StrutLayout.SPRING_BOTH);
231 }
232
233 protected void setupPanel()
234 {
235 MerlotDebug.msg("SetupPanel");
236
237 JComponent prev = null;
238 boolean newsubtext = false;
239
240 DTDAttribute a;
241
242 // get all the attributes and create a set of text fields,
243 // and labels
244 Enumeration e = _node.getDTDAttributes();
245 if (e != null) {
246 // MerlotDebug.msg("Got attributes");
247
248 int i = 0;
249
250 while (e.hasMoreElements()) {
251 a = (DTDAttribute)e.nextElement();
252 _dtd_attributes.put(a.getName(),a);
253 i++;
254 }
255
256 i = 0;
257
258 // hack to make sure name and label attributes show up first in the list
259 // XXX make this a configurable hack someday
260 a = (DTDAttribute) _dtd_attributes.get("name");
261 if (a != null) {
262 addAttribute(a);
263 i++;
264 }
265
266 a = (DTDAttribute) _dtd_attributes.get("label");
267 if (a != null) {
268 addAttribute(a);
269 i++;
270 }
271
272 e = _dtd_attributes.elements();
273 while (e.hasMoreElements()) {
274 a = (DTDAttribute) e.nextElement();
275 if (a.getName().equals("name") || a.getName().equals("label")) {
276 // skip it
277 } else {
278 if (!suppressAttribute(a)) {
279 addAttribute(a);
280 i++;
281 }
282 }
283 // MerlotDebug.msg("Added "+c+" to panel");
284 }
285 }
286
287 // see if this node has a #text node possible under it
288 MerlotDOMNode[] children = _node.getChildNodes();
289 for (int i=0; i < children.length; i++) {
290 if (children[i] instanceof MerlotDOMText) {
291 MerlotDOMText textNode = (MerlotDOMText)children[i];
292 // System.out.println("child["+i+"] = "+children[i]+" nodeValue='"+textNode.getText()+"'");
293 if (textNode.isVisible()) {
294 _subtext = textNode;
295 break;
296 }
297 }
298 }
299 // see if #text is a posibility
300 if (_subtext == null) {
301 Enumeration en = _node.getAppendableElements();
302 DTDElement el;
303 String nm;
304
305 while (en != null && en.hasMoreElements()) {
306 el = (DTDElement)en.nextElement();
307 if (el != null) {
308 nm = el.getName();
309 if (nm != null && nm.equals(DTDConstants.PCDATA_KEY)) {
310 newsubtext = true;
311 break;
312 }
313 }
314 }
315 }
316
317 /*=$**/
318 // pas affichage du composant associé a PCDATA dans attributs
319 /*
320 if (_subtext != null || newsubtext) {
321 String s = "";
322 if (!newsubtext) {
323 s = _subtext.getText();
324 }
325
326 _text = new JTextArea(s);
327 _text.setLineWrap(true);
328 _text.setWrapStyleWord(true);
329 _text.setMinimumSize(new Dimension(4,4));
330
331 JScrollPane sp = new JScrollPane(_text, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
332 sp.setPreferredSize(new Dimension(350,200));
333 JLabel l = new JLabel(DTDConstants.PCDATA_KEY + ":",JLabel.RIGHT);
334
335 addAttributeComponent(l,sp,ALIGN_TOP);
336 }
337 */
338 /*=$**/
339
340 }
341
342
343 /**
344 * returns true if the attribute doesn't show in the editor display
345 */
346 protected boolean suppressAttribute(DTDAttribute a)
347 {
348
349 String s = _node.getNodeName() + "." + a.getName();
350 String t = (String)_hideAttrs.get(s);
351 if (t != null) {
352 return true;
353 }
354 return false;
355
356 }
357
358 protected void setupHideTable()
359 {
360 String hidetypes = XMLEditorSettings.getSharedInstance().getProperty(HIDE_PROP);
361 _hideAttrs = new Hashtable();
362 if (hidetypes != null) {
363 // parse it
364 StringTokenizer tok = new StringTokenizer(hidetypes,", ");
365 while (tok.hasMoreTokens()) {
366 String s = tok.nextToken();
367 _hideAttrs.put(s,"yes");
368 }
369 }
370
371 }
372
373 protected void setupReadonlyTable()
374 {
375 String rotypes = XMLEditorSettings.getSharedInstance().
376 getProperty(RO_PROP);
377 // System.out.println("rotypes = "+rotypes);
378
379 _readonlyAttrs = new Hashtable();
380 if (rotypes != null) {
381 // parse it
382 StringTokenizer tok = new StringTokenizer(rotypes,", ");
383 while (tok.hasMoreTokens()) {
384 String s = tok.nextToken();
385 MerlotDebug.msg("adding "+s+" to readonly attributes");
386
387 _readonlyAttrs.put(s,"yes");
388 }
389 }
390
391 }
392
393
394 protected void addAttribute(DTDAttribute a)
395 {
396 if (a != null) {
397 JLabel l = new JLabel(a.getName() + ":",JLabel.RIGHT);
398 if (a.getDefaultType() == DTDAttribute.REQUIRED) {
399 Icon reqIcon = getRequiredAttrIcon();
400 if (reqIcon != null) {
401 l.setIcon(reqIcon);
402 }
403 }
404
405
406 JComponent c = getEditComponent(a);
407
408 if (c == null) {
409 return;
410 }
411 _attrComponents.put(a.getName(),c);
412
413
414 String s = _node.getNodeName() + "." + a.getName();
415 String t = (String)_readonlyAttrs.get(s);
416 if (t != null) {
417 // System.out.println("disabling component: "+c);
418
419 if (c instanceof JTextComponent) {
420 ((JTextComponent)c).setEditable(false);
421 }
422 c.setEnabled(false);
423 }
424 addAttributeComponent(l,c,ALIGN_MIDDLE);
425
426 }
427 }
428
429 protected Icon getRequiredAttrIcon()
430 {
431 if (_requiredAttrIcon == null) {
432 _requiredAttrIcon = MerlotResource.getImage(UI,"editor.required.attr.icon");
433 }
434 return _requiredAttrIcon;
435 }
436
437
438 protected void addAttributeComponent(JLabel l, JComponent c, int align) {
439
440
441 StrutLayout.StrutConstraint strut;
442 StrutLayout.StrutConstraint strut2;
443
444 if (_first_component) {
445 strut = new StrutLayout.StrutConstraint(_prev,StrutLayout.MID_RIGHT,StrutLayout.MID_LEFT,
446 StrutLayout.EAST,20);
447 strut2 = new StrutLayout.StrutConstraint(l,StrutLayout.MID_RIGHT, StrutLayout.MID_LEFT,
448 StrutLayout.EAST);
449 _attributePanel.add(l,strut);
450 _attributePanel.add(c,strut2);
451
452 _first_field = c;
453
454 _first_component = false;
455
456 }
457 else {
458 strut2 = new StrutLayout.StrutConstraint(_prev,StrutLayout.BOTTOM_LEFT,StrutLayout.TOP_LEFT,
459 StrutLayout.SOUTH);
460 switch (align) {
461 case ALIGN_TOP: // top
462 strut = new StrutLayout.StrutConstraint(c,StrutLayout.TOP_LEFT, StrutLayout.TOP_RIGHT,
463 StrutLayout.WEST);
464 break;
465 default:
466 case ALIGN_MIDDLE: // middle
467 strut = new StrutLayout.StrutConstraint(c,StrutLayout.MID_LEFT, StrutLayout.MID_RIGHT,
468 StrutLayout.WEST);
469 break;
470 case ALIGN_BOTTOM: // bot
471 strut = new StrutLayout.StrutConstraint(c,StrutLayout.BOTTOM_LEFT, StrutLayout.BOTTOM_RIGHT,
472 StrutLayout.WEST);
473 break;
474 }
475
476
477 _attributePanel.add(c,strut2);
478 _attributePanel.add(l,strut);
479
480 }
481 if (c instanceof JTextField) {
482 ((StrutLayout)_attributePanel.getLayout()).setSprings(c,StrutLayout.SPRING_HORIZ);
483 }
484 if (c instanceof JScrollPane) {
485 ((StrutLayout)_attributePanel.getLayout()).setSprings(c,StrutLayout.SPRING_BOTH);
486 }
487
488
489 _prev = c;
490
491 }
492
493
494
495
496 /**
497 * Create a component based on the attribute type, and get the default from
498 * the node, or if the node doesn't have it set, get the default value from
499 * the attribute definition itself
500 */
501 protected JComponent getEditComponent(DTDAttribute attr)
502 {
503 MerlotDebug.msg("getEditComponent("+attr+")");
504
505 int t = attr.getType();
506 JComponent ret = null;
507 String value = null;
508 Attr a = null;
509
510 if (_node_attributes != null) {
511 a = (Attr)_node_attributes.getNamedItem(attr.getName());
512 }
513
514 if (a != null) {
515 value = a.getValue();
516 }
517
518
519 switch (t) {
520 case DTDConstants.NMTOKEN:
521 case DTDConstants.CDATA:
522 ret = new JTextField();
523 if (value != null) {
524 ((JTextField)ret).setText(value);
525 }
526 else {
527 value = attr.getDefaultValue();
528 if (value != null) {
529 ((JTextField)ret).setText(value);
530 }
531 }
532 break;
533
534 case DTDConstants.ID:
535 ret = getIdComponent(_node,attr.getName());
536 break;
537 case DTDConstants.IDREF:
538 ret = getIdRefComponent(_node,attr.getName());
539 break;
540 case DTDConstants.TOKEN_GROUP:
541 Enumeration e = attr.getTokens();
542
543 boolean checkbox = true;
544
545 if (e != null) {
546 Vector v = new Vector();
547 if (attr.getDefaultValue() == null) {
548 v.addElement("");
549 }
550 while (e.hasMoreElements()) {
551 MerlotDebug.msg("v = " + v + " e = " + e);
552 Object o = e.nextElement();
553 String s = o.toString();
554
555 checkbox = checkbox && (s.equals("true") || s.equals("false"));
556
557 v.addElement(o);
558 }
559 if (v.size() == 2 && checkbox) {
560 ret = new JCheckBox();
561 ((JCheckBox) ret).setSelected(value != null && value.equals("true"));
562 break;
563 }
564
565 ret = new JComboBox(v);
566 ((JComboBox)ret).setEditable(false);
567 int i = getIndexInVector(v,value);
568 if (i < 0) {
569 i = getIndexInVector(v,attr.getDefaultValue());
570 }
571 if (i >= 0) {
572 ((JComboBox)ret).setSelectedIndex(i);
573 }
574
575 }
576 break;
577 default:
578 JLabel l = new JLabel(MerlotResource.getString(ERR,"xml.attr.type.unknown"));
579 ret = l;
580 break;
581
582 }
583 return ret;
584 }
585
586
587
588
589 /** Saves any changes back to the DOM */
590 public void save()
591 throws PropertyVetoException
592 {
593 HashMap t = new HashMap();
594 save(t);
595 }
596
597 protected void save(HashMap attributes)
598 throws PropertyVetoException
599 {
600 if (_text != null) {
601 if (_text.getText().trim().equals("")) {
602 if (_subtext != null) {
603 // it's empty and they didn't create it or edit it
604 // manually... remove it
605 _node.removeChild(_subtext);
606 }
607 } else {
608 if (_subtext == null) {
609 // create a new TextNode
610 MerlotDOMNode nd = _node.newChild(DTDConstants.PCDATA_KEY);
611 if (nd instanceof MerlotDOMText) {
612 _subtext = (MerlotDOMText)nd;
613 }
614 }
615 if (_subtext != null) {
616 _subtext.setText(_text.getText());
617 }
618 }
619 }
620
621 // put together a hashtable of attributes to pass back to the node
622 Enumeration e = _attrComponents.keys();
623 while (e.hasMoreElements()) {
624 String key = (String)e.nextElement();
625 DTDAttribute dtdAttr = (DTDAttribute)_dtd_attributes.get(key);
626 Node oldnode = _node_attributes.getNamedItem(key);
627 String oldval;
628 if (oldnode != null) {
629 oldval = oldnode.getNodeValue();
630 } else {
631 oldval = "";
632 }
633 JComponent c = (JComponent)_attrComponents.get(key);
634 String newval = null;
635 if (c instanceof JTextField) {
636 newval = ((JTextField)c).getText();
637 } else if (c instanceof JComboBox) {
638 Object item = ((JComboBox)c).getSelectedItem();
639 if (item != null) {
640 newval = item.toString().trim();
641 }
642 } else if (c instanceof JCheckBox) {
643 newval = ((JCheckBox) c).isSelected() ? "true" : "false";
644 }else {
645 // nothing to do now
646 MerlotDebug.msg("Unknown editing component in GenericDOMEditPanel.save: "+c);
647 if (attributes.containsKey(key)) {
648 newval = (String)attributes.get(key);
649 }
650 }
651 if (newval != null && newval.trim().equals("")) {
652 newval = null;
653 }
654 if (newval == null && dtdAttr != null && dtdAttr.getDefaultType() == DTDAttribute.REQUIRED) {
655 String err[] = new String[2];
656 err[0] = _node.getNodeName();
657 err[1] = key;
658 throw new PropertyVetoException(MessageFormat.format(MerlotResource.getString(ERR,"required.field"),err),new PropertyChangeEvent(_node,key,oldval,newval));
659 }
660
661 fireVetoableChange(new PropertyChangeEvent(_node,key,oldval,newval));
662
663 // check if the attribute is already set in the hashtable
664 if (!attributes.containsKey(key)) {
665 attributes.put(key,newval);
666 }
667
668 }
669 _node.setAttributes(attributes);
670 }
671
672 public void grabFocus()
673 {
674 if (_first_field != null) {
675 _first_field.grabFocus();
676 }
677 }
678
679
680
681 // search through the vector for the value we want
682 /*=$**/
683 // passage de methode en public pour pouvoir s'ens ervir dans GenDiapoEditPanel
684 //private int getIndexInVector(Vector v, String s)
685 public int getIndexInVector(Vector v, String s)
686 /*=$**/
687 {
688 if (v != null && s != null) {
689 int len = v.size();
690 for (int i = 0; i < len; i++) {
691 Object o = v.elementAt(i);
692 if (o.equals(s)) {
693 return i;
694 }
695 }
696 }
697
698 return -1;
699 }
700
701 public void addVetoableChangeListener (VetoableChangeListener l)
702 {
703 _vetoListeners.addElement(l);
704
705 }
706 public void removeVetoableChangeListener (VetoableChangeListener l)
707 {
708 _vetoListeners.removeElement(l);
709 }
710
711 // gotta implement this stuff ourselves cause the PropertyChangeSupport
712 // classes compare the old and new values, and dont' fire if they're equal
713 public void fireVetoableChange(PropertyChangeEvent evt)
714 throws PropertyVetoException
715 {
716 // System.out.println("fireVetoableChange: "+evt);
717
718 Enumeration e = _vetoListeners.elements();
719 while (e.hasMoreElements()) {
720
721 VetoableChangeListener l = (VetoableChangeListener)e.nextElement();
722 // System.out.println(" listener: "+l);
723 l.vetoableChange(evt);
724 }
725
726
727 }
728
729 protected IDManager getIdManager () {
730 return _node.getIdManager();
731 }
732
733 /**
734 * Returns a component aimed at editing the ID attribute from a DOM node.
735 *
736 * @param node the node for which to generate the ID editing component
737 * @param attrName the name of the ID attribute for which to generate the ID editing component
738 */
739 protected JComponent getIdComponent (MerlotDOMNode node, String attrName)
740 {
741 String value = null;
742
743 Attr attr = (Attr)node.getAttributes().getNamedItem(attrName);
744 if (attr != null) {
745 value = attr.getValue();
746 }
747
748 JTextField ret = new JTextField();
749 if (value == null) {
750 value = getIdManager().getDefaultIdValue(node,attrName);
751 }
752 if (value != null) {
753 ret.setText(value);
754 }
755 return ret;
756 }
757
758 /**
759 * Returns a component aimed at editing the IDREF attribute from a DOM node.
760 *
761 * @param node the node for which to generate the IDREF editing component
762 * @param attrName the name of the IDREF attribute for which to generate the ID editing component
763 */
764 protected JComponent getIdRefComponent (MerlotDOMNode targetNode, String targetAttrName)
765 {
766 String value = null;
767
768 Attr targetAttr = (Attr)targetNode.getAttributes().getNamedItem(targetAttrName);
769 if (targetAttr != null) {
770 value = targetAttr.getValue();
771 }
772
773 JComboBox choices = new JComboBox();
774 choices.setRenderer(new IDREFComboBoxRenderer());
775 choices.setEditable(false);
776 int selectedValue = 0;
777
778 Hashtable idAttrs = getIdManager().getIDAttrs(targetNode, targetAttrName);
779 if (idAttrs != null) {
780 Enumeration e = idAttrs.keys();
781 int i = 0;
782 while (e.hasMoreElements()) {
783 Attr attr = (Attr)e.nextElement();
784 MerlotDOMNode node = (MerlotDOMNode)idAttrs.get(attr);
785 String id = getIdForNode( node );
786 if (IdAttributesAreCompatible(targetNode, targetAttrName, node, attr.getName())) {
787 IDObject idObject = new IDObject ( id,
788 getDisplayTextForAttribute(targetNode,
789 targetAttrName,
790 node,
791 attr.getName()));
792 choices.addItem(idObject);
793 if (id.equals(value))
794 selectedValue = i;
795 i++;
796 }
797 }
798 if (idAttrs.size()!=0)
799 choices.setSelectedIndex(selectedValue);
800 }
801 return choices;
802 }
803
804 public String getIdForNode( MerlotDOMNode node )
805 {
806 // Get the first attribute of type "ID"
807 Enumeration e = node.getDTDAttributes();
808 // Set the default to name "id" in case it is not found in the DTD
809 String idAttributeName = "id";
810 if (e != null) {
811 while (e.hasMoreElements()) {
812 DTDAttribute dtdAttr = (DTDAttribute)e.nextElement();
813 if (dtdAttr.getType() == DTDConstants.ID) {
814 idAttributeName = dtdAttr.getName();
815 break;
816 }
817 }
818 }
819 String id = ((String)( (org.w3c.dom.Element)(node.getRealNode()) ).getAttribute(idAttributeName)).trim();
820 return id;
821 }
822
823
824 /**
825 * Returns whether or not the value of an ID attribute can be used as
826 * a value of a target IDREF attribute.
827 * By default in XML 1.0 specification, all ID values can be used for IDREFs.
828 * This decision can be constrained by subclassing this method.
829 *
830 * @param idRefNode node containing the IDREF attribute
831 * @param idRefAttrName name of the IDREF attribute
832 * @param idNode node containing the ID attribute
833 * @param idAttrName name of the ID attribute
834 */
835 public boolean IdAttributesAreCompatible (MerlotDOMNode idRefNode, String idRefAttrName, MerlotDOMNode IdNode, String idAttrName)
836 {
837 return true;
838 }
839
840 /**
841 * Returns the text which represents the referenced node in an IDREF comboBox
842 */
843 protected String getDisplayTextForAttribute (MerlotDOMNode idRefNode, String idRefAttrName, MerlotDOMNode idNode, String idAttrName)
844 {
845 Attr attr = (Attr)idNode.getAttributes().getNamedItem(idAttrName);
846 String ret = "";
847 if (attr != null) {
848 ret = attr.getValue();
849 }
850 return ret;
851 }
852
853
854
855 // A special renderer for the combobox to enable a different value
856 // to be displayed from the value that gets saved.
857 /*=$*/public/*=$*/ class IDREFComboBoxRenderer extends JLabel implements ListCellRenderer
858 {
859 public IDREFComboBoxRenderer()
860 {
861 setOpaque(true);
862 }
863 public Component getListCellRendererComponent( JList list,
864 Object value,
865 int index,
866 boolean isSelected,
867 boolean cellHasFocus)
868 {
869 if (isSelected) {
870 setBackground(list.getSelectionBackground());
871 setForeground(list.getSelectionForeground());
872 } else {
873 setBackground(list.getBackground());
874 setForeground(list.getForeground());
875 }
876 IDObject idObject = (IDObject)value;
877 String display = (idObject != null)?idObject.getDisplayText():null;
878 setText((display==null)? "" : display);
879 return this;
880 }
881 }
882
883 /*=$*/public /*=$*/ class IDObject {
884 protected String _idValue = "";
885 protected String _displayText = "";
886
887 public IDObject (String idValue, String displayText) {
888 if (idValue!=null)
889 _idValue = idValue.trim();
890 if (displayText!=null)
891 _displayText = displayText.trim();
892 }
893
894 public String getIdValue () {
895 return _idValue;
896 }
897
898 public String toString() {
899 return _idValue;
900 }
901
902 public String getDisplayText () {
903 return _displayText;
904 }
905 }
906
907 /**
908 * Checks attributes according to their type. E.g. NMTOKEN is only of the characters specified
909 * by the XML spec
910 */
911 protected class StandardAttributeChecker implements VetoableChangeListener
912 {
913 public void vetoableChange(PropertyChangeEvent evt)
914 throws PropertyVetoException
915 {
916 Object n = evt.getSource();
917 if (!(n instanceof MerlotDOMNode)) {
918 return;
919 }
920 MerlotDOMNode node = (MerlotDOMNode)n;
921
922 String attributeName = evt.getPropertyName();
923 Object o = evt.getNewValue();
924 String value = "";
925 if (o instanceof String) {
926 value = ((String)o).trim();
927 }
928
929
930 // [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
931 // [5] Name ::= (Letter | '_' | ':') (NameChar)*
932 // [6] Names ::= Name (S Name)*
933 // [7] Nmtoken ::= (NameChar)+
934 // [8] Nmtokens ::= Nmtoken (S Nmtoken)*
935
936
937
938 DTDAttribute attribute = (DTDAttribute)_dtd_attributes.get(attributeName);
939 if (attribute != null) {
940 int t = attribute.getType();
941 char invalid = 0;
942 switch (t) {
943 case DTDConstants.NMTOKEN:
944
945 // inspect the value and make sure it conforms to the restrictions on NMTOKEN's
946 if (value.length() == 0) {
947 throw new PropertyVetoException("NMTOKEN's require a value",evt);
948 }
949 invalid = checkNmtokenChars(value);
950 break;
951
952 case DTDConstants.CDATA:
953 break;
954
955 case DTDConstants.ID:
956 break;
957 case DTDConstants.IDREF:
958 break;
959 case DTDConstants.TOKEN_GROUP:
960 break;
961
962 }
963 if (invalid != 0) {
964 String[] err = new String[3];
965 err[0] = node.getNodeName();
966 err[1] = attribute.getName();
967 err[2] = "'" + invalid + "'";
968
969 throw new PropertyVetoException(MessageFormat.format(MerlotResource.getString(ERR,"illegal.value.attr.char"),err),evt);
970
971 }
972
973 }
974 }
975
976
977
978 }
979
980 /*
981 * [7] Nmtoken ::= (NameChar)+
982 */
983 /**
984 * Check to see if a string is a valid Nmtoken according to [7]
985 * in the XML 1.0 Recommendation
986 *
987 * @param nmtoken string to checj
988 * @return the first invalid char or 0
989 */
990 // borrowed from Xerces cause we need to know what char is invalid
991 public static char checkNmtokenChars(String nmtoken)
992 {
993 org.apache.xerces.utils.XMLCharacterProperties.initCharFlags();
994
995 for (int i = 0; i < nmtoken.length(); i++) {
996 char ch = nmtoken.charAt(i);
997 if (ch > 'z') {
998 if ((org.apache.xerces.utils.XMLCharacterProperties.fgCharFlags[ch] & org.apache.xerces.utils.XMLCharacterProperties.E_NameCharFlag) == 0)
999 return ch;
1000
1001 } else if (org.apache.xerces.utils.XMLCharacterProperties.fgAsciiNameChar[ch] == 0) {
1002 return ch;
1003 }
1004 }
1005 return 0;
1006 }
1007
1008 public static class ScrollablePanel extends JPanel implements Scrollable
1009 {
1010
1011 private boolean _trackHeight = true;
1012 private boolean _trackWidth = true;
1013
1014 public ScrollablePanel (boolean trackWidth, boolean trackHeight)
1015 {
1016 _trackWidth = trackWidth;
1017 _trackHeight = trackHeight;
1018 }
1019
1020
1021 public Dimension getPreferredScrollableViewportSize()
1022 {
1023 return getPreferredSize();
1024 }
1025
1026
1027 public int getScrollableUnitIncrement(Rectangle visibleRect,
1028 int orientation,
1029 int direction)
1030 {
1031 return 15;
1032 }
1033
1034
1035
1036
1037 public int getScrollableBlockIncrement(Rectangle visibleRect,
1038 int orientation,
1039 int direction)
1040 {
1041 return 30;
1042 }
1043
1044
1045 public boolean getScrollableTracksViewportWidth()
1046 {
1047 return _trackWidth;
1048 }
1049
1050
1051 public boolean getScrollableTracksViewportHeight()
1052 {
1053 return _trackHeight;
1054 }
1055
1056
1057 }
1058}