001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.core.text;
011:
012: import java.util.ArrayList;
013: import java.util.Iterator;
014: import java.util.TreeMap;
015:
016: import org.eclipse.core.runtime.CoreException;
017: import org.eclipse.pde.core.IModel;
018: import org.eclipse.pde.internal.core.XMLPrintHandler;
019: import org.eclipse.pde.internal.core.util.PDETextHelper;
020:
021: public abstract class DocumentElementNode extends DocumentXMLNode
022: implements IDocumentElementNode {
023:
024: private static final long serialVersionUID = 1L;
025:
026: public static final String ATTRIBUTE_VALUE_ENCODING = "UTF-8"; //$NON-NLS-1$
027:
028: public static final String ATTRIBUTE_VALUE_TRUE = "true"; //$NON-NLS-1$
029:
030: public static final String ATTRIBUTE_VALUE_FALSE = "false"; //$NON-NLS-1$
031:
032: private transient IDocumentElementNode fParent;
033: private transient boolean fIsErrorNode;
034: private transient int fLength;
035: private transient int fOffset;
036: private transient IDocumentElementNode fPreviousSibling;
037: private transient int fIndent;
038:
039: private ArrayList fChildren;
040: private TreeMap fAttributes;
041: private String fTag;
042: private IDocumentTextNode fTextNode;
043:
044: // TODO: MP: TEO: LOW: Regenerate comments
045:
046: /**
047: *
048: */
049: public DocumentElementNode() {
050: fParent = null;
051: fIsErrorNode = false;
052: fLength = -1;
053: fOffset = -1;
054: fPreviousSibling = null;
055: fIndent = 0;
056:
057: fChildren = new ArrayList();
058: fAttributes = new TreeMap();
059: fTag = null;
060: fTextNode = null;
061: }
062:
063: /* (non-Javadoc)
064: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#getChildNodesList()
065: */
066: public ArrayList getChildNodesList() {
067: // Not used by text edit operations
068: return fChildren;
069: }
070:
071: /* (non-Javadoc)
072: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#getNodeAttributesMap()
073: */
074: public TreeMap getNodeAttributesMap() {
075: // Not used by text edit operations
076: return fAttributes;
077: }
078:
079: /* (non-Javadoc)
080: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#writeShallow(boolean)
081: */
082: public String writeShallow(boolean terminate) {
083: // Used by text edit operations
084: StringBuffer buffer = new StringBuffer();
085: // Print opening angle bracket
086: buffer.append("<"); //$NON-NLS-1$
087: // Print element
088: buffer.append(getXMLTagName());
089: // Print attributes
090: buffer.append(writeAttributes());
091: // Make self-enclosing element if specified
092: if (terminate) {
093: buffer.append("/"); //$NON-NLS-1$
094: }
095: // Print closing angle bracket
096: buffer.append(">"); //$NON-NLS-1$
097:
098: return buffer.toString();
099: }
100:
101: /* (non-Javadoc)
102: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#isContentCollapsed()
103: */
104: public boolean isLeafNode() {
105: return false;
106: }
107:
108: public boolean canTerminateStartTag() {
109: if ((hasXMLChildren() == false) && (hasXMLContent() == false)
110: && (isLeafNode() == true)) {
111: return true;
112: }
113: return false;
114: }
115:
116: /* (non-Javadoc)
117: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#write(boolean)
118: */
119: public String write(boolean indent) {
120: // Used by text edit operations
121: // TODO: MP: TEO: LOW: Refactor into smaller methods
122: // TODO: MP: TEO: LOW: Do we care about the indent flag? If so make consistent with write attributes and content
123: StringBuffer buffer = new StringBuffer();
124: boolean hasChildren = hasXMLChildren();
125: boolean hasContent = hasXMLContent();
126: boolean terminate = canTerminateStartTag();
127:
128: // Print XML decl if root
129: if (isRoot()) {
130: buffer.append(writeXMLDecl());
131: }
132: // Print indent
133: if (indent) {
134: buffer.append(getIndent());
135: }
136: // Print start element and attributes
137: buffer.append(writeShallow(terminate));
138: // Print child elements
139: if (hasChildren) {
140: IDocumentElementNode[] children = getChildNodes();
141: for (int i = 0; i < children.length; i++) {
142: children[i].setLineIndent(getLineIndent() + 3);
143: buffer.append(getLineDelimiter()
144: + children[i].write(true));
145: }
146: }
147: // Print text content
148: if (hasContent) {
149: buffer.append(writeXMLContent());
150: }
151: // Print end element
152: // TODO: MP: TEO: LOW: Replace with XMLPrintHandler constants
153: if (terminate == false) {
154: buffer.append(getLineDelimiter() + getIndent());
155: buffer.append("</"); //$NON-NLS-1$
156: buffer.append(getXMLTagName());
157: buffer.append(">"); //$NON-NLS-1$
158: }
159:
160: return buffer.toString();
161: }
162:
163: protected String writeXMLContent() {
164: StringBuffer buffer = new StringBuffer();
165: if (isDefined(fTextNode)) {
166: buffer.append(getContentIndent());
167: buffer.append(fTextNode.write());
168: }
169: return buffer.toString();
170: }
171:
172: protected String writeAttributes() {
173: StringBuffer buffer = new StringBuffer();
174: IDocumentAttributeNode[] attributes = getNodeAttributes();
175: // Write all attributes
176: for (int i = 0; i < attributes.length; i++) {
177: IDocumentAttributeNode attribute = attributes[i];
178: if (isDefined(attribute)) {
179: buffer.append(getAttributeIndent() + attribute.write());
180: }
181: }
182: return buffer.toString();
183: }
184:
185: /* (non-Javadoc)
186: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#getChildNodes()
187: */
188: public IDocumentElementNode[] getChildNodes() {
189: // Used by text edit operations
190: return (IDocumentElementNode[]) fChildren
191: .toArray(new IDocumentElementNode[fChildren.size()]);
192: }
193:
194: /* (non-Javadoc)
195: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#indexOf(org.eclipse.pde.internal.ui.model.IDocumentNode)
196: */
197: public int indexOf(IDocumentElementNode child) {
198: // Not used by text edit operations
199: return fChildren.indexOf(child);
200: }
201:
202: /* (non-Javadoc)
203: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getChildAt(int)
204: */
205: public IDocumentElementNode getChildAt(int index) {
206: // Used by text edit operations
207: if (index < fChildren.size())
208: return (IDocumentElementNode) fChildren.get(index);
209: return null;
210: }
211:
212: /* (non-Javadoc)
213: * @see org.eclipse.pde.internal.ui.neweditor.model.IDocumentNode#getParentNode()
214: */
215: public IDocumentElementNode getParentNode() {
216: // Used by text edit operations
217: return fParent;
218: }
219:
220: /* (non-Javadoc)
221: * @see org.eclipse.pde.internal.ui.neweditor.model.IDocumentNode#setParentNode(org.eclipse.pde.internal.ui.neweditor.model.IDocumentNode)
222: */
223: public void setParentNode(IDocumentElementNode node) {
224: // Used by text edit operations (indirectly)
225: fParent = node;
226: }
227:
228: /* (non-Javadoc)
229: * @see org.eclipse.pde.internal.ui.neweditor.model.IDocumentNode#addChildNode(org.eclipse.pde.internal.ui.neweditor.model.IDocumentNode)
230: */
231: public void addChildNode(IDocumentElementNode child) {
232: // Used by text edit operations
233: addChildNode(child, fChildren.size());
234: }
235:
236: /* (non-Javadoc)
237: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#addChildNode(org.eclipse.pde.internal.ui.model.IDocumentNode, int)
238: */
239: public void addChildNode(IDocumentElementNode child, int position) {
240: // Used by text edit operations
241: fChildren.add(position, child);
242: child.setParentNode(this );
243: linkNodeWithSiblings(child);
244: }
245:
246: /* (non-Javadoc)
247: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#removeChildNode(org.eclipse.pde.internal.ui.model.IDocumentNode)
248: */
249: public IDocumentElementNode removeChildNode(
250: IDocumentElementNode child) {
251: // Used by text edit operations
252: int index = fChildren.indexOf(child);
253: if (index != -1) {
254: fChildren.remove(child);
255: if (index < fChildren.size()) {
256: IDocumentElementNode prevSibling = index == 0 ? null
257: : (IDocumentElementNode) fChildren
258: .get(index - 1);
259: ((IDocumentElementNode) fChildren.get(index))
260: .setPreviousSibling(prevSibling);
261: return child;
262: }
263: }
264: return null;
265: }
266:
267: /* (non-Javadoc)
268: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#removeChildNode(org.eclipse.pde.internal.ui.model.IDocumentNode)
269: */
270: public IDocumentElementNode removeChildNode(int index) {
271: // NOT used by text edit operations
272: if ((index < 0) || (index >= fChildren.size())) {
273: return null;
274: }
275: // Get the child at the specified index
276: IDocumentElementNode child = (IDocumentElementNode) fChildren
277: .get(index);
278: // Remove the child
279: fChildren.remove(child);
280: // Determine the new previous sibling for the new element at the
281: // specified index
282: if (index < fChildren.size()) {
283: IDocumentElementNode previousSibling = null;
284: if (index != 0) {
285: previousSibling = (IDocumentElementNode) fChildren
286: .get(index - 1);
287: }
288: IDocumentElementNode newNode = (IDocumentElementNode) fChildren
289: .get(index);
290: newNode.setPreviousSibling(previousSibling);
291: }
292: return child;
293: }
294:
295: /* (non-Javadoc)
296: * @see org.eclipse.pde.internal.ui.neweditor.model.IDocumentNode#isErrorNode()
297: */
298: public boolean isErrorNode() {
299: // Used by text edit operations (indirectly)
300: return fIsErrorNode;
301: }
302:
303: /* (non-Javadoc)
304: * @see org.eclipse.pde.internal.ui.neweditor.model.IDocumentNode#setIsErrorNode(boolean)
305: */
306: public void setIsErrorNode(boolean isErrorNode) {
307: // Used by text edit operations
308: fIsErrorNode = isErrorNode;
309: }
310:
311: /* (non-Javadoc)
312: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#setOffset(int)
313: */
314: public void setOffset(int offset) {
315: // Used by text edit operations
316: fOffset = offset;
317: }
318:
319: /* (non-Javadoc)
320: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#setLength(int)
321: */
322: public void setLength(int length) {
323: // Used by text edit operations
324: fLength = length;
325: }
326:
327: /* (non-Javadoc)
328: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getOffset()
329: */
330: public int getOffset() {
331: // Used by text edit operations
332: return fOffset;
333: }
334:
335: /* (non-Javadoc)
336: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getLength()
337: */
338: public int getLength() {
339: // Used by text edit operations
340: return fLength;
341: }
342:
343: /* (non-Javadoc)
344: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#setAttribute(org.eclipse.pde.internal.ui.model.IDocumentAttribute)
345: */
346: public void setXMLAttribute(IDocumentAttributeNode attribute) {
347: // Used by text edit operations
348: fAttributes.put(attribute.getAttributeName(), attribute);
349: }
350:
351: /* (non-Javadoc)
352: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getXMLAttributeValue(java.lang.String)
353: */
354: public String getXMLAttributeValue(String name) {
355: // Not used by text edit operations
356: IDocumentAttributeNode attribute = (IDocumentAttributeNode) fAttributes
357: .get(name);
358: if (attribute == null) {
359: return null;
360: }
361: return attribute.getAttributeValue();
362: }
363:
364: /* (non-Javadoc)
365: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#setXMLTagName(java.lang.String)
366: */
367: public void setXMLTagName(String tag) {
368: // Used by text edit operations (indirectly)
369: fTag = tag;
370: }
371:
372: /* (non-Javadoc)
373: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getXMLTagName()
374: */
375: public String getXMLTagName() {
376: // Used by text edit operations
377: return fTag;
378: }
379:
380: /* (non-Javadoc)
381: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getDocumentAttribute(java.lang.String)
382: */
383: public IDocumentAttributeNode getDocumentAttribute(String name) {
384: // Used by text edit operations
385: return (IDocumentAttributeNode) fAttributes.get(name);
386: }
387:
388: /* (non-Javadoc)
389: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getLineIndent()
390: */
391: public int getLineIndent() {
392: // Used by text edit operations
393: return fIndent;
394: }
395:
396: /* (non-Javadoc)
397: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#setLineIndent(int)
398: */
399: public void setLineIndent(int indent) {
400: // Used by text edit operations
401: fIndent = indent;
402: }
403:
404: /* (non-Javadoc)
405: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getAttributes()
406: */
407: public IDocumentAttributeNode[] getNodeAttributes() {
408: // Used by text edit operations
409: ArrayList list = new ArrayList();
410: Iterator iter = fAttributes.values().iterator();
411: while (iter.hasNext())
412: list.add(iter.next());
413: return (IDocumentAttributeNode[]) list
414: .toArray(new IDocumentAttributeNode[list.size()]);
415: }
416:
417: /* (non-Javadoc)
418: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getPreviousSibling()
419: */
420: public IDocumentElementNode getPreviousSibling() {
421: // Used by text edit operations
422: return fPreviousSibling;
423: }
424:
425: /* (non-Javadoc)
426: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#setPreviousSibling(org.eclipse.pde.internal.ui.model.IDocumentNode)
427: */
428: public void setPreviousSibling(IDocumentElementNode sibling) {
429: // Used by text edit operations
430: fPreviousSibling = sibling;
431: }
432:
433: /**
434: * @return
435: */
436: public String getIndent() {
437: StringBuffer buffer = new StringBuffer();
438: for (int i = 0; i < fIndent; i++) {
439: buffer.append(" "); //$NON-NLS-1$
440: }
441: return buffer.toString();
442: }
443:
444: /* (non-Javadoc)
445: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#swap(org.eclipse.pde.internal.ui.model.IDocumentNode, org.eclipse.pde.internal.ui.model.IDocumentNode)
446: */
447: public void swap(IDocumentElementNode child1,
448: IDocumentElementNode child2) {
449: // Not used by text edit operations
450: int index1 = fChildren.indexOf(child1);
451: int index2 = fChildren.indexOf(child2);
452:
453: fChildren.set(index1, child2);
454: fChildren.set(index2, child1);
455:
456: child1.setPreviousSibling(index2 == 0 ? null
457: : (IDocumentElementNode) fChildren.get(index2 - 1));
458: child2.setPreviousSibling(index1 == 0 ? null
459: : (IDocumentElementNode) fChildren.get(index1 - 1));
460:
461: if (index1 < fChildren.size() - 1)
462: ((IDocumentElementNode) fChildren.get(index1 + 1))
463: .setPreviousSibling(child2);
464:
465: if (index2 < fChildren.size() - 1)
466: ((IDocumentElementNode) fChildren.get(index2 + 1))
467: .setPreviousSibling(child1);
468: }
469:
470: /* (non-Javadoc)
471: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#addTextNode(org.eclipse.pde.internal.ui.model.IDocumentTextNode)
472: */
473: public void addTextNode(IDocumentTextNode textNode) {
474: // Used by text edit operations
475: fTextNode = textNode;
476: }
477:
478: /* (non-Javadoc)
479: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getTextNode()
480: */
481: public IDocumentTextNode getTextNode() {
482: // Used by text edit operations
483: return fTextNode;
484: }
485:
486: /* (non-Javadoc)
487: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#removeTextNode()
488: */
489: public void removeTextNode() {
490: // Used by text edit operations
491: fTextNode = null;
492: }
493:
494: /* (non-Javadoc)
495: * @see org.eclipse.pde.internal.ui.model.IDocumentNode#removeDocumentAttribute(org.eclipse.pde.internal.ui.model.IDocumentAttribute)
496: */
497: public void removeDocumentAttribute(IDocumentAttributeNode attr) {
498: // Used by text edit operations
499: fAttributes.remove(attr.getAttributeName());
500: }
501:
502: /* (non-Javadoc)
503: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#reconnectRoot(org.eclipse.pde.core.plugin.ISharedPluginModel)
504: */
505: public void reconnect(IDocumentElementNode parent, IModel model) {
506: // Not used by text edit operations
507: // Reconnect XML document characteristics
508: reconnectDocument();
509: // Reconnect parent
510: reconnectParent(parent);
511: // Reconnect previous sibling
512: reconnectPreviousSibling();
513: // Reconnect text node
514: reconnectText();
515: // Reconnect attribute nodes
516: reconnectAttributes();
517: // Reconnect children nodes
518: reconnectChildren(model);
519: }
520:
521: /**
522: * @param model
523: * @param schema
524: */
525: private void reconnectAttributes() {
526: // Get all attributes
527: Iterator keys = fAttributes.keySet().iterator();
528: // Fill in appropriate transient field values for all attributes
529: while (keys.hasNext()) {
530: String key = (String) keys.next();
531: IDocumentAttributeNode attribute = (IDocumentAttributeNode) fAttributes
532: .get(key);
533: attribute.reconnect(this );
534: }
535: }
536:
537: /**
538: * @param model
539: * @param schema
540: */
541: private void reconnectChildren(IModel model) {
542: // Fill in appropriate transient field values
543: for (int i = 0; i < fChildren.size(); i++) {
544: IDocumentElementNode child = (IDocumentElementNode) fChildren
545: .get(i);
546: // Reconnect child
547: child.reconnect(this , model);
548: }
549: }
550:
551: /**
552: *
553: */
554: private void reconnectDocument() {
555: // Transient field: Indent
556: fIndent = 0;
557: // Transient field: Error Node
558: fIsErrorNode = false;
559: // Transient field: Length
560: fLength = -1;
561: // Transient field: Offset
562: fOffset = -1;
563: }
564:
565: /**
566: * @param parent
567: */
568: private void reconnectParent(IDocumentElementNode parent) {
569: // Transient field: Parent
570: fParent = parent;
571: }
572:
573: /**
574: * @param parent
575: */
576: private void reconnectPreviousSibling() {
577: // Transient field: Previous Sibling
578: linkNodeWithSiblings(this );
579: }
580:
581: /**
582: * PRE: Node must have a set parent
583: * @param node
584: */
585: private void linkNodeWithSiblings(IDocumentElementNode targetNode) {
586: // Get the node's parent
587: IDocumentElementNode parentNode = targetNode.getParentNode();
588: // Ensure we have a parent
589: if (parentNode == null) {
590: return;
591: }
592: // Get the position of the node in the parent's children
593: int targetNodePosition = parentNode.indexOf(targetNode);
594: // Get the number of children the parent has (including the node)
595: int parentNodeChildCount = parentNode.getChildCount();
596: // Set this node's previous sibling as the node before it
597: if (targetNodePosition <= 0) {
598: // null <- targetNode <- ?
599: targetNode.setPreviousSibling(null);
600: } else if ((targetNodePosition >= 1)
601: && (parentNodeChildCount >= 2)) {
602: // ? <- previousNode <- targetNode <- ?
603: IDocumentElementNode previousNode = parentNode
604: .getChildAt(targetNodePosition - 1);
605: targetNode.setPreviousSibling(previousNode);
606: }
607: int secondLastNodeIndex = parentNodeChildCount - 2;
608: // Set the node after this node's previous sibling as this node
609: if ((targetNodePosition >= 0)
610: && (targetNodePosition <= secondLastNodeIndex)
611: && (parentNodeChildCount >= 2)) {
612: // ? <- targetNode <- nextNode <- ?
613: IDocumentElementNode nextNode = parentNode
614: .getChildAt(targetNodePosition + 1);
615: nextNode.setPreviousSibling(targetNode);
616: }
617: // previousNode <- targetNode <- nextNode
618: }
619:
620: /**
621: *
622: */
623: private void reconnectText() {
624: // Transient field: Text Node
625: if (fTextNode != null) {
626: fTextNode.reconnect(this );
627: }
628: }
629:
630: /* (non-Javadoc)
631: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#getChildCount()
632: */
633: public int getChildCount() {
634: // Not used by text edit operations
635: return fChildren.size();
636: }
637:
638: /* (non-Javadoc)
639: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#isRoot()
640: */
641: public boolean isRoot() {
642: // Used by text edit operations
643: return false;
644: }
645:
646: protected String getFileEncoding() {
647: return ATTRIBUTE_VALUE_ENCODING;
648: }
649:
650: protected String writeXMLDecl() {
651: StringBuffer buffer = new StringBuffer(XMLPrintHandler.XML_HEAD);
652: buffer.append(getFileEncoding());
653: buffer.append(XMLPrintHandler.XML_DBL_QUOTES);
654: buffer.append(XMLPrintHandler.XML_HEAD_END_TAG);
655: buffer.append(getLineDelimiter());
656: return buffer.toString();
657: }
658:
659: protected String getAttributeIndent() {
660: return getLineDelimiter() + getIndent() + " "; //$NON-NLS-1$
661: }
662:
663: protected String getContentIndent() {
664: // TODO: MP: TEO: LOW: Add indent methods on documenttextnode?
665: return getLineDelimiter() + getIndent() + " "; //$NON-NLS-1$
666: }
667:
668: protected String getLineDelimiter() {
669: // Subclasses to override
670: return System.getProperty("line.separator"); //$NON-NLS-1$
671: }
672:
673: /**
674: * @param attribute
675: * @return
676: */
677: protected boolean isDefined(IDocumentAttributeNode attribute) {
678: if (attribute == null) {
679: return false;
680: } else if (attribute.getAttributeValue().trim().length() <= 0) {
681: return false;
682: }
683: return true;
684: }
685:
686: /**
687: * @param node
688: * @return
689: */
690: protected boolean isDefined(IDocumentTextNode node) {
691: if (node == null) {
692: return false;
693: }
694: return PDETextHelper.isDefinedAfterTrim(node.getText());
695: }
696:
697: /* (non-Javadoc)
698: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#hasXMLChildren()
699: */
700: public boolean hasXMLChildren() {
701: if (getChildCount() == 0) {
702: return false;
703: }
704: return true;
705: }
706:
707: /* (non-Javadoc)
708: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#hasXMLContent()
709: */
710: public boolean hasXMLContent() {
711: if (isDefined(fTextNode)) {
712: return true;
713: }
714: return false;
715: }
716:
717: /* (non-Javadoc)
718: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#getNodeAttributesCount()
719: */
720: public int getNodeAttributesCount() {
721: // Returns the number of attributes with defined values
722: int count = 0;
723: IDocumentAttributeNode[] attributes = getNodeAttributes();
724: for (int i = 0; i < attributes.length; i++) {
725: IDocumentAttributeNode attribute = attributes[i];
726: if (isDefined(attribute)) {
727: count++;
728: }
729: }
730: return count;
731: }
732:
733: /* (non-Javadoc)
734: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#hasXMLAttributes()
735: */
736: public boolean hasXMLAttributes() {
737: if (getNodeAttributesCount() == 0) {
738: return false;
739: }
740: return true;
741: }
742:
743: /* (non-Javadoc)
744: * @see org.eclipse.pde.internal.core.text.plugin.PluginDocumentNode#setXMLAttribute(java.lang.String, java.lang.String)
745: */
746: public boolean setXMLAttribute(String name, String value) {
747: // Not used by text edit operations
748:
749: // Ensure name is defined
750: if ((name == null) || (name.length() == 0)) {
751: return false;
752: }
753: // Null values are not allowed
754: if (value == null) {
755: value = ""; //$NON-NLS-1$
756: }
757: String oldValue = getXMLAttributeValue(name);
758: // Check if the value is different
759: if ((oldValue != null) && oldValue.equals(value)) {
760: return false;
761: }
762: // Check to see if the attribute already exists
763: IDocumentAttributeNode attribute = (IDocumentAttributeNode) getNodeAttributesMap()
764: .get(name);
765: try {
766: if (attribute == null) {
767: // Attribute does not exist
768: attribute = createDocumentAttributeNode();
769: attribute.setAttributeName(name);
770: attribute.setEnclosingElement(this );
771: setXMLAttribute(attribute);
772: }
773: // Update the value
774: attribute.setAttributeValue(value);
775: } catch (CoreException e) {
776: // Ignore
777: return false;
778: }
779: return true;
780: }
781:
782: /* (non-Javadoc)
783: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#setXMLContent(java.lang.String)
784: */
785: public boolean setXMLContent(String text) {
786: // Not used by text edit operations
787: // Null text not allowed
788: if (text == null) {
789: text = ""; //$NON-NLS-1$
790: }
791: // Check to see if the node already exists
792: IDocumentTextNode node = getTextNode();
793: if (node == null) {
794: // Text does not exist, create it
795: node = createDocumentTextNode();
796: node.setEnclosingElement(this );
797: addTextNode(node);
798: }
799: // Update text on node
800: node.setText(text);
801: // Always changed
802: return true;
803: }
804:
805: /* (non-Javadoc)
806: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#getXMLContent()
807: */
808: public String getXMLContent() {
809: IDocumentTextNode node = getTextNode();
810: if (node == null) {
811: // No text node
812: return null;
813: }
814: return node.getText();
815: }
816:
817: /* (non-Javadoc)
818: * @see org.eclipse.pde.internal.core.text.DocumentXMLNode#write()
819: */
820: public String write() {
821: return write(false);
822: }
823:
824: /* (non-Javadoc)
825: * @see org.eclipse.pde.internal.core.text.IDocumentXMLNode#getXMLType()
826: */
827: public int getXMLType() {
828: return F_TYPE_ELEMENT;
829: }
830:
831: /* (non-Javadoc)
832: * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#isContentCollapsed()
833: */
834: public boolean isContentCollapsed() {
835: return false;
836: }
837:
838: /**
839: * @return
840: */
841: protected IDocumentAttributeNode createDocumentAttributeNode() {
842: return new DocumentAttributeNode();
843: }
844:
845: /**
846: * @return
847: */
848: protected IDocumentTextNode createDocumentTextNode() {
849: return new DocumentTextNode();
850: }
851:
852: }
|