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.tax;
042:
043: import java.util.Iterator;
044:
045: import org.netbeans.tax.spec.Element;
046: import org.netbeans.tax.spec.Attribute;
047:
048: /**
049: * TreeAtribute represents attribute name value pair.
050: * Value of attribute can be: Text or EntityReference.
051: *
052: * @author Libor Kramolis
053: * @version 0.1
054: */
055: public class TreeAttribute extends TreeNode implements
056: Element.Attribute, TreeNamedObjectMap.NamedObject {
057:
058: /** */
059: public static final String PROP_NAME = "name"; // NOI18N
060: /** */
061: public static final String PROP_VALUE = "value"; // NOI18N
062: /** */
063: public static final String PROP_OWNER_ELEMENT = "ownerElement"; // NOI18N
064: /** */
065: public static final String PROP_SPECIFIED = "specified"; // NOI18N
066:
067: /** -- can be null. */
068: private TreeElement ownerElement; //my "parent" -- element in which it is attribute // NOI18N
069:
070: /** */
071: private TreeName name; //attribute qName
072:
073: /** */
074: private TreeObjectList valueList;
075:
076: /** */
077: private boolean specified; //is the attribute specified in document? (or default)
078:
079: /** */
080: private TreeNamedObjectMap.KeyListener mapKeyListener;
081:
082: //
083: // init
084: //
085:
086: /**
087: * Creates new TreeAttribute.
088: * @param qName XML qualified name e.g. "myns:root" or "root".
089: * @param value unnormalized attribute value (general refs allowed ???)
090: * @param specified true means that the attribute must be represented literaly in document
091: * @throws InvalidArgumentException if qName or value contains unacceptable values
092: */
093: public TreeAttribute(String qName, String value, boolean specified)
094: throws InvalidArgumentException {
095: super ();
096:
097: TreeName treeName = new TreeName(qName);
098: checkName(treeName);
099: checkValue(value);
100:
101: this .name = treeName;
102: this .specified = specified;
103: this .valueList = new TreeObjectList(
104: createValueListContentManager());
105: setValueImpl(value);
106:
107: if (Util.THIS.isLoggable()) /* then */
108: Util.THIS.debug("TreeAttribute::INIT : name = " + qName
109: + " : specified = " + specified); // NOI18N
110: }
111:
112: /**
113: * Creates new specified TreeAttribute.
114: * @param qName XML qualified name e.g. "myns:root" or "root".
115: * @param value unnormalized attribute value (no general refs allowed)
116: * @throws InvalidArgumentException if qName or value contains unacceptable values
117: */
118: public TreeAttribute(String qName, String value)
119: throws InvalidArgumentException {
120: this (qName, value, true);
121: }
122:
123: /**
124: * Creates new TreeAttribute -- copy constructor.
125: */
126: protected TreeAttribute(TreeAttribute attribute) {
127: super (attribute);
128:
129: this .name = attribute.name;
130: this .specified = true; //??? -- copy will be specified
131: this .valueList = new TreeObjectList(
132: createValueListContentManager());
133: this .valueList.addAll((TreeObjectList) attribute.valueList
134: .clone());
135: }
136:
137: //
138: // from TreeObject
139: //
140:
141: /**
142: */
143: public Object clone() {
144: return new TreeAttribute(this );
145: }
146:
147: /**
148: */
149: public boolean equals(Object object, boolean deep) {
150: if (!!!super .equals(object, deep))
151: return false;
152:
153: TreeAttribute peer = (TreeAttribute) object;
154: if (!!!Util.equals(this .getTreeName(), peer.getTreeName()))
155: return false;
156: if (this .specified != peer.isSpecified())
157: return false;
158: if (!!!Util.equals(this .valueList, peer.valueList))
159: return false;
160:
161: return true;
162: }
163:
164: /*
165: * Merge name and specified (sticky) properties and delegate value list merging.
166: */
167: public void merge(TreeObject treeObject)
168: throws CannotMergeException {
169: super .merge(treeObject);
170:
171: TreeAttribute peer = (TreeAttribute) treeObject;
172:
173: try {
174: setTreeNameImpl(peer.getTreeName());
175: setSpecifiedImpl(peer.isSpecified());
176: valueList.merge(peer.valueList);
177: } catch (Exception exc) {
178: throw new CannotMergeException(treeObject, exc);
179: }
180: }
181:
182: //
183: // read only
184: //
185:
186: /**
187: */
188: protected void setReadOnly(boolean newReadOnly) {
189: super .setReadOnly(newReadOnly);
190:
191: valueList.setReadOnly(newReadOnly);
192: }
193:
194: //
195: // context
196: //
197:
198: /**
199: */
200: public final boolean isInContext() {
201: return (getOwnerElement() != null);
202: }
203:
204: /**
205: */
206: public final void removeFromContext() throws ReadOnlyException {
207: if (isInContext()) {
208: getOwnerElement().removeAttribute(this );
209: }
210: }
211:
212: //
213: // itself
214: //
215:
216: /**
217: */
218: public final String getQName() {
219: return name.getQualifiedName();
220: }
221:
222: /**
223: * @throws ReadOnlyException
224: * @throws InvalidArgumentException if given name is not acceptable by constains
225: */
226: public final void setQName(String name) throws ReadOnlyException,
227: InvalidArgumentException {
228: setTreeName(new TreeName(name));
229: }
230:
231: /**
232: */
233: public final TreeName getTreeName() {
234: return name;
235: }
236:
237: /**
238: */
239: private final void setTreeNameImpl(TreeName newName) {
240: TreeName oldName = this .name;
241:
242: this .name = newName;
243:
244: fireMapKeyChanged(oldName);
245: firePropertyChange(PROP_NAME, oldName, newName);
246: }
247:
248: /**
249: * @throws ReadOnlyException
250: * @throws InvalidArgumentException if passed argument does not pass checks
251: */
252: public final void setTreeName(TreeName newName)
253: throws ReadOnlyException, InvalidArgumentException {
254: //
255: // check new value
256: //
257: if (Util.equals(this .name, newName))
258: return;
259: checkReadOnly();
260: checkName(newName);
261:
262: //
263: // set new value
264: //
265: setTreeNameImpl(newName);
266: }
267:
268: /**
269: */
270: protected final void checkName(TreeName name)
271: throws InvalidArgumentException {
272: TreeUtilities.checkAttributeName(name);
273: }
274:
275: public boolean isSpecified() {
276: return specified;
277: }
278:
279: /**
280: * Set the value and fire a property change event.
281: * It may change just during merge operation.
282: */
283: private void setSpecifiedImpl(boolean newValue) {
284: if (this .specified == newValue)
285: return;
286:
287: Boolean oldValue = this .specified ? Boolean.TRUE
288: : Boolean.FALSE;
289:
290: this .specified = newValue;
291:
292: firePropertyChange(PROP_SPECIFIED, oldValue,
293: newValue ? Boolean.TRUE : Boolean.FALSE);
294: }
295:
296: /**
297: * @return structured representation of attribute value.
298: */
299: public final TreeObjectList getValueList() {
300: return valueList;
301: }
302:
303: /**
304: * @return resolved attribute text value
305: */
306: public final String getValue() {
307: StringBuffer value = new StringBuffer(23);
308: Iterator it = valueList.iterator();
309:
310: while (it.hasNext()) {
311: Object next = it.next();
312: if (next instanceof TreeData) {
313: value.append(((TreeData) next).getData());
314: } else if (next instanceof TreeGeneralEntityReference) {
315: //!!! resolve it
316: value.append("&"
317: + ((TreeGeneralEntityReference) next).getName()
318: + ";"); // NOI18N
319: } else if (next instanceof TreeCharacterReference) {
320: value.append(((TreeCharacterReference) next).getData());
321: }
322: }
323: return value.toString();
324: }
325:
326: /**
327: * @return unresolved attribute value
328: */
329: public final String getNonNormalizedValue() {
330: StringBuffer value = new StringBuffer(23);
331: Iterator it = valueList.iterator();
332:
333: while (it.hasNext()) {
334: Object next = it.next();
335: if (next instanceof TreeData) {
336: value.append(((TreeData) next).getData());
337: } else if (next instanceof TreeGeneralEntityReference) {
338: value.append("&"
339: + ((TreeGeneralEntityReference) next).getName()
340: + ";"); // NOI18N
341: } else if (next instanceof TreeCharacterReference) {
342: value.append("&"
343: + ((TreeCharacterReference) next).getName()
344: + ";"); // NOI18N
345: }
346: }
347: return value.toString();
348: }
349:
350: /**
351: * Simplified attribute value setter.
352: *
353: */
354: private final void setValueImpl(String newValue) {
355: String oldValue = this .getValue();
356:
357: this .valueList.clear();
358:
359: if (newValue.length() != 0) {
360: try {
361: TreeText newText = new TreeText(newValue);
362: this .valueList.add(newText);
363: } catch (TreeException exc) {
364: // something is wrong -- OK
365: }
366: }
367:
368: firePropertyChange(PROP_VALUE, oldValue, newValue);
369: }
370:
371: /**
372: * Simplified attribute value setter.
373: *
374: * @throws ReadOnlyException
375: * @throws InvalidArgumentException
376: */
377: public final void setValue(String newValue)
378: throws ReadOnlyException, InvalidArgumentException {
379: //
380: // check new value
381: //
382: if (Util.equals(this .getValue(), newValue))
383: return;
384: checkReadOnly();
385: checkValue(newValue);
386:
387: //
388: // set new value
389: //
390: setValueImpl(newValue);
391: }
392:
393: /**
394: * Check value being set by setValue
395: */
396: protected final void checkValue(String value)
397: throws InvalidArgumentException {
398: TreeUtilities.checkAttributeValue(value);
399: }
400:
401: //
402: // Namespaces
403: //
404:
405: /**
406: * @return attribute namespace or TreeNamespace.NO_NAMESPACE.
407: */
408: public final TreeNamespace getNamespace() {
409: if (getOwnerElement() != null) {
410: TreeElement owner = getOwnerElement();
411: TreeNamespaceContext ctx = owner.getNamespaceContext();
412: String prefix = getNamespacePrefix();
413: String uri = ctx.getURI(prefix);
414: if (uri == null) {
415: return TreeNamespace.NO_NAMESPACE;
416: } else {
417: return new TreeNamespace(prefix, uri);
418: }
419: }
420: return TreeNamespace.NO_NAMESPACE;
421: }
422:
423: /**
424: */
425: public final String getNamespacePrefix() {
426: return name.getPrefix();
427: }
428:
429: /**
430: */
431: public final String getNamespaceURI() {
432: return getNamespace().getURI();
433: }
434:
435: /**
436: */
437: public final String getLocalName() {
438: return name.getName();
439: }
440:
441: //
442: // TreeNamedObjectMap.NamedObject
443: //
444:
445: /**
446: */
447: public Object mapKey() {
448: return getTreeName();
449: }
450:
451: /**
452: */
453: // public String mapKeyPropertyName () {
454: // return PROP_NAME;
455: // }
456: /** Attach NamedObject to NamedObject Map. */
457: public void setKeyListener(
458: TreeNamedObjectMap.KeyListener keyListener) {
459: mapKeyListener = keyListener;
460: }
461:
462: private void fireMapKeyChanged(Object oldKey) {
463: if (mapKeyListener == null) {
464: return;
465: }
466: mapKeyListener.mapKeyChanged(oldKey);
467: }
468:
469: //
470: // from TreeNode
471: //
472:
473: /**
474: */
475: public final TreeDocumentRoot getOwnerDocument() {
476: if (getOwnerElement() == null)
477: return null;
478: return getOwnerElement().getOwnerDocument();
479: }
480:
481: //
482: // ownerElement
483: //
484:
485: /**
486: */
487: public final TreeElement getOwnerElement() {
488: return ownerElement;
489: }
490:
491: /**
492: */
493: protected final void setOwnerElement(TreeElement newOwnerElement) {
494: if (Util.equals(ownerElement, newOwnerElement))
495: return;
496:
497: TreeElement oldOwnerElement = this .ownerElement;
498:
499: this .ownerElement = newOwnerElement;
500:
501: firePropertyChange(getEventChangeSupport().createEvent(
502: PROP_OWNER_ELEMENT, oldOwnerElement, newOwnerElement));
503: }
504:
505: //
506: // TreeObjectList.ContentManager
507: //
508:
509: /**
510: */
511: protected TreeObjectList.ContentManager createValueListContentManager() {
512: return new ValueListContentManager();
513: }
514:
515: /**
516: *
517: */
518: protected class ValueListContentManager extends
519: TreeObjectList.ContentManager {
520:
521: /**
522: */
523: public TreeNode getOwnerNode() {
524: return TreeAttribute.this ;
525: }
526:
527: /**
528: */
529: public void checkAssignableObject(Object obj) {
530: super .checkAssignableObject(obj);
531: checkAssignableClass(Attribute.Value.class, obj);
532: }
533:
534: /** */
535: public void objectInserted(TreeObject obj) {
536: TreeAttribute.this .firePropertyChange(PROP_VALUE,
537: TreeAttribute.this .valueList, obj); //!!!
538: }
539:
540: /** */
541: public void objectRemoved(TreeObject obj) {
542: TreeAttribute.this .firePropertyChange(PROP_VALUE,
543: TreeAttribute.this .valueList, obj); //!!!
544: }
545:
546: /** */
547: public void orderChanged(int[] permutation) {
548: TreeAttribute.this .firePropertyChange(PROP_VALUE,
549: TreeAttribute.this .valueList, permutation); //!!!
550: }
551:
552: } // end: class ValueListContentManager
553:
554: }
|