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-2007 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.modules.visualweb.designer.markup;
042:
043: import java.util.logging.Level;
044: import java.util.logging.Logger;
045: import org.netbeans.modules.visualweb.api.designer.cssengine.CssProvider;
046: import org.netbeans.modules.visualweb.designer.html.HtmlTag;
047: import com.sun.rave.designtime.markup.MarkupDesignBean;
048: import java.lang.reflect.Field;
049: import org.apache.xerces.dom.AttrImpl;
050: import org.apache.xerces.dom.AttrNSImpl;
051: import org.apache.xerces.dom.CoreDocumentImpl;
052: import org.apache.xerces.dom.DOMMessageFormatter;
053:
054: import org.apache.xerces.dom.DocumentImpl;
055: import org.apache.xerces.xni.NamespaceContext;
056: import org.w3c.dom.Attr;
057: import org.w3c.dom.DOMException;
058: import org.w3c.dom.Element;
059: import org.w3c.dom.Node;
060: import org.w3c.dom.NodeList;
061: import org.w3c.dom.Text;
062:
063: // CVS note: This file used to be called XhtmlDocument (same directory)
064: // if you need to look at older CVS history
065:
066: /**
067: * This class provides a DocumentImpl for use with xerces, but which instead
068: * of ElementImpl produces XhtmlElementImpl objects.
069: *
070: * @author Tor Norbye
071: */
072: public abstract class AbstractRaveDocument extends DocumentImpl /*implements ParsingDocument*/{
073: /**
074: *
075: */
076: private static final long serialVersionUID = 3979265845992305975L;
077:
078: /**
079: * Non-NS elements should not be created; we don't want to mix
080: * these with NS-elements since some document Edwin pointed me to
081: * describes that this is a risky thing to do.
082: * So we force the created element to be a NS element.
083: *
084: * @param tagName The name of the element type to instantiate. For
085: * XML, this is case-sensitive. For HTML, the tagName parameter may
086: * be provided in any case, but it must be mapped to the canonical
087: * uppercase form by the DOM implementation.
088: *
089: * @throws DOMException(INVALID_NAME_ERR) if the tag name is not
090: * acceptable.
091: */
092: public Element createElement(String tagName) throws DOMException {
093: // TODO - should I look for a colon and if so, split out the end
094: // as a local name?
095: //return createElementNS(null, tagName);
096: // XXX What namespace should we put here? Arguably, xhtml!
097: // Since namespaceless tags are probably html tag elements
098: // See http://www.w3.org/TR/REC-xml-names/#defaulting for more.
099: return createElementNS(NamespaceContext.XML_URI, tagName);
100: }
101:
102: /**
103: * Introduced in DOM Level 2. <p>
104: * Creates an element of the given qualified name and namespace URI.
105: * If the given namespaceURI is null or an empty string and the
106: * qualifiedName has a prefix that is "xml", the created element
107: * is bound to the predefined namespace
108: * "http://www.w3.org/XML/1998/namespace" [Namespaces].
109: * @param namespaceURI The namespace URI of the element to
110: * create.
111: * @param qualifiedName The qualified name of the element type to
112: * instantiate.
113: * @return Element A new Element object with the following attributes:
114: * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified
115: * name contains an invalid character.
116: * @throws DOMException NAMESPACE_ERR: Raised if the qualifiedName has a
117: * prefix that is "xml" and the namespaceURI is
118: * neither null nor an empty string nor
119: * "http://www.w3.org/XML/1998/namespace", or
120: * if the qualifiedName has a prefix different
121: * from "xml" and the namespaceURI is null or an
122: * empty string.
123: * @since WD-DOM-Level-2-19990923
124: */
125: public Element createElementNS(String namespaceURI,
126: String qualifiedName) throws DOMException {
127: Element element = createSpecialElements(namespaceURI,
128: qualifiedName);
129: if (element != null) {
130: return element;
131: }
132: if (namespaceURI == null && qualifiedName.indexOf(':') != -1) {
133: // Happens for for example jsp:include.directive
134: namespaceURI = NamespaceContext.XML_URI;
135: }
136: // return new RaveElementImpl(this, namespaceURI, qualifiedName);
137: return createDefaultElement(this , namespaceURI, qualifiedName);
138: }
139:
140: /**
141: * NON-DOM: a factory method used by the Xerces DOM parser
142: * to create an element.
143: *
144: * @param namespaceURI The namespace URI of the element to
145: * create.
146: * @param qualifiedName The qualified name of the element type to
147: * instantiate.
148: * @param localpart The local name of the attribute to instantiate.
149: *
150: * @return Element A new Element object with the following attributes:
151: * @exception DOMException INVALID_CHARACTER_ERR: Raised if the specified
152: * name contains an invalid character.
153: */
154: public Element createElementNS(String namespaceURI,
155: String qualifiedName, String localpart) throws DOMException {
156:
157: Element element = createSpecialElements(namespaceURI,
158: qualifiedName);
159: if (element != null) {
160: return element;
161: }
162: // return new RaveElementImpl(this, namespaceURI, qualifiedName, localpart);
163: return createDefaultElement(this , namespaceURI, qualifiedName,
164: localpart);
165: }
166:
167: /** Construct special elements for some tags */
168: private Element createSpecialElements(String namespaceURI,
169: String qualifiedName) {
170: char firstChar = qualifiedName.charAt(0);
171: switch (firstChar) {
172: case 's':
173: if (qualifiedName.equals(HtmlTag.STYLE.name)) {
174: // StyleElement e = new StyleElement(this, namespaceURI, qualifiedName);
175: Element e = createStyleElement(this , namespaceURI,
176: qualifiedName);
177: // XhtmlCssEngine engine = ((RaveDocument)e.getOwnerDocument()).getCssEngine();
178: // if (engine != null) {
179: // engine.addTransientStyleSheetNode(e);
180: // }
181: CssProvider.getEngineService()
182: .addTransientStyleSheetNodeForDocument(this , e);
183: return e;
184: }
185: break;
186: case 't':
187: if (qualifiedName.equals(HtmlTag.TABLE.name)) {
188: // return new RaveTableElementImpl(this, namespaceURI, qualifiedName);
189: return createTableElement(this , namespaceURI,
190: qualifiedName);
191: }
192: break;
193: case 'l':
194: if (qualifiedName.equals(HtmlTag.LINK.name)) {
195: // TODO - should we enforce that "rel" is "stylesheet"
196: // (case insensitively) and "type" is "text/css"
197: // and "href" is set?
198: // return new StylesheetLinkElement(this, namespaceURI, qualifiedName);
199: return createStylesheetLinkElement(this , namespaceURI,
200: qualifiedName);
201: }
202: }
203: return null;
204: }
205:
206: /**
207: * Factory method; creates a Text node having this Document as its
208: * OwnerDoc.
209: *
210: * @param data The initial contents of the Text.
211: */
212: public Text createTextNode(String data) {
213: // return new RaveTextImpl(this, data);
214: return createTextNode(this , data);
215: }
216:
217: // public Text createJspxTextNode(String data) {
218: // RaveText text = new RaveText(this, data);
219: // text.setJspx(true);
220: // return text;
221: // }
222:
223: public Node importNode(Node source, boolean deep)
224: throws DOMException {
225: Node copied = super .importNode(source, deep);
226: duplicateXhtmlInfo(source, copied, deep);
227: return copied;
228: }
229:
230: /** For a cloned tree, update the xhtml info. We couldn't just
231: * override clone because xerces' importNode implementation doesn't
232: * use it.
233: */
234: private void duplicateXhtmlInfo(Node src, Node dst, boolean deep) {
235: // if (src instanceof RaveElement) {
236: // assert dst instanceof RaveElement;
237: // RaveElement srcElement = (RaveElement)src;
238: // RaveElement dstElement = (RaveElement)dst;
239: // ((RaveElementImpl)dstElement).copyFrom((RaveElementImpl)srcElement);
240: // //dstElement.source = srcElement.getSourceNode();
241: // dstElement.setSource(srcElement);
242: // } else if (src instanceof RaveText) {
243: // assert dst instanceof RaveText;
244: // RaveText srcText = (RaveText)src;
245: // RaveText dstText = (RaveText)dst;
246: // ((RaveTextImpl)dstText).copyFrom((RaveTextImpl)srcText);
247: // dstText.setSource(srcText);
248: // }
249: // if (src instanceof RaveSourceElement) {
250: if (src instanceof Element
251: && src.getOwnerDocument() instanceof RaveSourceDocument) {
252: // assert dst instanceof RaveRenderedElement;
253: // RaveSourceElement srcElement = (RaveSourceElement)src;
254: Element srcElement = (Element) src;
255:
256: if (dst instanceof RaveElement) {
257: ((RaveElement) dst).copyFrom((RaveElement) srcElement);
258: }
259:
260: // if (dst instanceof RaveRenderedElement) {
261: if (dst instanceof Element
262: && dst.getOwnerDocument() instanceof RaveRenderedDocument) {
263: //dstElement.source = srcElement.getSourceNode();
264: // ((RaveRenderedElement)dst).linkToSourceElement(srcElement);
265: MarkupServiceImpl.linkToSourceElement((Element) dst,
266: srcElement);
267: }
268: // } else if (src instanceof RaveSourceText) {
269: } else if (src instanceof Text
270: && src.getOwnerDocument() instanceof RaveSourceDocument) {
271: // assert dst instanceof RaveRenderedText;
272: // RaveSourceText srcText = (RaveSourceText)src;
273: Text srcText = (Text) src;
274:
275: if (dst instanceof RaveText) {
276: ((RaveText) dst).copyFrom((RaveText) srcText);
277: }
278: // if (dst instanceof RaveRenderedText) {
279: if (dst instanceof Text
280: && dst.getOwnerDocument() instanceof RaveRenderedDocument) {
281: // ((RaveRenderedText)dst).linkToSourceText(srcText);
282: MarkupServiceImpl.linkToSourceText((Text) dst, srcText);
283: }
284: // } else if (src instanceof RaveRenderedElement) {
285: } else if (src instanceof Element
286: && src.getOwnerDocument() instanceof RaveRenderedDocument) {
287: // XXX Prerendered elements - see doc jsp writer, and inline editing.
288: // RaveSourceElement srcElement = ((RaveRenderedElement)src).getSourceElement();
289: Element srcElement = MarkupServiceImpl
290: .getSourceElement((Element) src);
291:
292: // if (srcElement != null) {
293: if (srcElement instanceof RaveElement) {
294: if (dst instanceof RaveElement) {
295: ((RaveElement) dst)
296: .copyFrom((RaveElement) srcElement);
297: }
298:
299: // if (dst instanceof RaveRenderedElement) {
300: if (dst instanceof Element
301: && dst.getOwnerDocument() instanceof RaveRenderedDocument) {
302: //dstElement.source = srcElement.getSourceNode();
303: // ((RaveRenderedElement)dst).linkToSourceElement(srcElement);
304: MarkupServiceImpl.linkToSourceElement(
305: (Element) dst, srcElement);
306: }
307: }
308: }
309: // else if (src instanceof RaveRenderedText) {
310: // RaveSourceText srcText = ((RaveRenderedTextImpl)src).sourceText;
311: //
312: // if (srcText != null) {
313: // if (dst instanceof AbstractRaveText) {
314: // ((AbstractRaveText)dst).copyFrom((AbstractRaveText)srcText);
315: // }
316: // if (dst instanceof RaveRenderedText) {
317: // ((RaveRenderedText)dst).setSource(srcText);
318: // }
319: // }
320: // }
321:
322: if (deep) {
323: NodeList srcChildren = src.getChildNodes();
324: NodeList dstChildren = dst.getChildNodes();
325: int len = srcChildren.getLength();
326: assert dstChildren.getLength() == len;
327:
328: for (int i = 0; i < len; i++) {
329: duplicateXhtmlInfo(srcChildren.item(i), dstChildren
330: .item(i), deep);
331: }
332: }
333: }
334:
335: // Moved to MarkupServiceImpl
336: // /**
337: // * Given two matching node trees where one represents a tree of
338: // * nodes rendered from the other, update the source and render references
339: // * in the nodes such that the "src" tree is marked as the source nodes
340: // * for "dst".
341: // */
342: // public static void markRendered(Node src, Node dst) {
343: // if (src instanceof RaveElement) {
344: // assert dst instanceof RaveElement;
345: // RaveElement srcElement = (RaveElement)src;
346: // RaveElement dstElement = (RaveElement)dst;
347: // srcElement.source = null;
348: // dstElement.source = srcElement;
349: // dstElement.setSource(srcElement);
350: // } else if (src instanceof RaveText) {
351: // assert dst instanceof RaveText;
352: // RaveText srcText = (RaveText)src;
353: // RaveText dstText = (RaveText)dst;
354: // srcText.source = null;
355: // dstText.source = srcText;
356: // dstText.setSource(srcText);
357: // }
358: // NodeList srcChildren = src.getChildNodes();
359: // NodeList dstChildren = dst.getChildNodes();
360: // int len = srcChildren.getLength();
361: // assert dstChildren.getLength() == len;
362: //
363: // for (int i = 0; i < len; i++) {
364: // markRendered(srcChildren.item(i), dstChildren.item(i));
365: // }
366: // }
367:
368: // Moving the func into MarkupUnit#getUrlForDocument.
369: // public void setUrl(URL url) {
370: // this.url = url;
371: // }
372: //
373: // public URL getUrl() {
374: // return url;
375: // }
376:
377: // There is already a getXmlEncoding method see xerces/CoreDocumentImpl.
378: // public String getEncoding() {
379: // return encoding;
380: // }
381:
382: // public XhtmlCssEngine getCssEngine() {
383: // return engine;
384: // }
385: //
386: // public void setCssEngine(XhtmlCssEngine engine) {
387: // this.engine = engine;
388: // }
389:
390: // /** Clear document related errors.
391: // * @param delayed When set, don't actually clear the errors right now;
392: // * it clears the errors next time another error is added. */
393: // public static void clearErrors(boolean delayed) {
394: // if (delayed) {
395: // clearErrors = true;
396: // } else {
397: // OutputWriter out = getOutputWriter();
398: // try {
399: // out.reset();
400: // }
401: // catch (IOException ioe) {
402: // // This is lame - our own output window shouldn't
403: // // throw IO exceptions!
404: // ErrorManager.getDefault().notify(ioe);
405: // }
406: // }
407: // }
408: //
409: // private static boolean clearErrors = false;
410: //
411: // private static OutputWriter getOutputWriter() {
412: // InputOutput io = IOProvider.getDefault().getIO(NbBundle.getMessage(RaveDocument.class, "WindowTitle"), false);
413: // OutputWriter out = io.getOut();
414: // return out;
415: // }
416: //
417: // /**
418: // * Display the given error message to the user. The optional listener argument
419: // * (pass in null if not applicable) will make the line hyperlinked and the
420: // * listener is invoked to process any user clicks.
421: // * @param message The string to be displayed to the user
422: // * @param listener null, or a listener to be notified when the user clicks
423: // * the linked message
424: // */
425: // public static void displayError(String message, OutputListener listener) {
426: // OutputWriter out = getOutputWriter();
427: // try {
428: // if (clearErrors) {
429: // out.reset();
430: // clearErrors = false;
431: // }
432: // // Write the error message to the output tab:
433: // out.println(message, listener);
434: // }
435: // catch (IOException ioe) {
436: // // This is lame - our own output window shouldn't throw IO exceptions!
437: // ErrorManager.getDefault().notify(ioe);
438: // }
439: // }
440: //
441: // /**
442: // * Cause the panel/window within which errors are displayed to come to the front if possible.
443: // *
444: // */
445: // public static void selectErrors() {
446: // InputOutput io = IOProvider.getDefault().getIO(NbBundle.getMessage(RaveDocument.class, "WindowTitle"), false);
447: // io.select();
448: // }
449: //
450: // /** Display an error message for the given source element. The error
451: // * will be clickable.
452: // */
453: // public static void displayError(final org.openide.filesystems.FileObject fileObject,
454: // final int lineNumber,
455: // String message) {
456: //// final XhtmlElement e = Util.getSource(element);
457: // OutputListener listener = new OutputListener() {
458: // public void outputLineSelected(OutputEvent ev) {
459: // }
460: // public void outputLineAction(OutputEvent ev) {
461: //// Util.show(null, unit.getFileObject(), unit.getLine(e),
462: //// 0, true);
463: // // <markup_separation>
464: //// Util.show(null, fileObject, lineNumber, 0, true);
465: // // ====
466: // MarkupUtilities.show(null, fileObject, lineNumber, 0, true);
467: // // </markup_separation>
468: // }
469: // public void outputLineCleared (OutputEvent ev) {
470: // }
471: // };
472: // displayError(message, listener);
473: // }
474:
475: // public void setRoot(RaveElement root) {
476: // this.root = root;
477: // }
478: //
479: // /**
480: // * Return the root element for the document, which may be rendered
481: // * @todo Rename to getEffectiveRoot() ?
482: // */
483: // public RaveElement getRoot() {
484: // if (root != null) {
485: // return root;
486: // } else {
487: // return (RaveElement)getDocumentElement();
488: // }
489: // }
490:
491: // // ----------- Implements ParsingDocument ------------------------
492: //
493: // public void appendParsedString(Node parent, String xhtml, MarkupDesignBean bean) {
494: // // <markup_separation>
495: //// markup.appendParsedString(parent, xhtml, bean);
496: // // ====
497: // InSyncService.getProvider().appendParsedString(this, parent, xhtml, bean);
498: // // </markup_separation>
499: // }
500:
501: // <markup_separation> See MarkupUnit#raveDoc2markupUnit in insync
502: // void setMarkupUnit(MarkupUnit markup) {
503: // this.markup = markup;
504: // }
505: //
506: // /** Return the associated markup unit - IF ANY */
507: // public MarkupUnit getMarkup() {
508: // return markup;
509: // }
510: // </markup_separation>
511:
512: // private URL url;
513: // private XhtmlCssEngine engine;
514: // <markup_separation>
515: // private MarkupUnit markup;
516: // </markup_separation>
517: // private RaveElement root;
518:
519: protected abstract Element createDefaultElement(
520: CoreDocumentImpl document, String namespaceURI,
521: String qualifiedName);
522:
523: protected abstract Element createDefaultElement(
524: CoreDocumentImpl document, String namespaceURI,
525: String qualifiedName, String localpart);
526:
527: protected abstract Element createStyleElement(
528: CoreDocumentImpl document, String namespaceURI,
529: String qualifiedName);
530:
531: protected abstract Element createTableElement(
532: CoreDocumentImpl document, String namespaceURI,
533: String qualifiedName);
534:
535: protected abstract Element createStylesheetLinkElement(
536: CoreDocumentImpl document, String namespaceURI,
537: String qualifiedName);
538:
539: protected abstract Text createTextNode(CoreDocumentImpl document,
540: String data);
541:
542: @Override
543: public Attr createAttribute(String name) throws DOMException {
544: if (errorChecking
545: && !isXMLName(name,/*xml11Version*/isXml11Version())) {
546: String msg = DOMMessageFormatter.formatMessage(
547: DOMMessageFormatter.DOM_DOMAIN,
548: "INVALID_CHARACTER_ERR", null);
549: throw new DOMException(DOMException.INVALID_CHARACTER_ERR,
550: msg);
551: }
552: return new Fix105085AttrImpl(this , name);
553: }
554:
555: @Override
556: public Attr createAttributeNS(String namespaceUri,
557: String qualifiedName) throws DOMException {
558: return new Fix105085AttrNSImpl(this , namespaceUri,
559: qualifiedName);
560: }
561:
562: @Override
563: public Attr createAttributeNS(String namespaceUri,
564: String qualifiedName, String localPart) throws DOMException {
565: return new Fix105085AttrNSImpl(this , namespaceUri,
566: qualifiedName, localPart);
567: }
568:
569: private boolean isXml11Version() {
570: try {
571: Field field = CoreDocumentImpl.class
572: .getDeclaredField("xml11Version"); // NOI18N
573: field.setAccessible(true);
574: return field.getBoolean(this );
575: } catch (IllegalArgumentException ex) {
576: log(ex);
577: } catch (IllegalAccessException ex) {
578: log(ex);
579: } catch (NoSuchFieldException ex) {
580: log(ex);
581: } catch (SecurityException ex) {
582: log(ex);
583: }
584: return false;
585: }
586:
587: private static class Fix105085AttrImpl extends AttrImpl {
588: public Fix105085AttrImpl(CoreDocumentImpl doc, String name) {
589: super (doc, name);
590: }
591:
592: @Override
593: public void setValue(String value) {
594: super .setValue(value);
595: // XXX #105085 Not storing the cached node, and forcing it to recreate each time.
596: textNode = null;
597: }
598: } // End of FixAttrImpl.
599:
600: private static class Fix105085AttrNSImpl extends AttrNSImpl {
601: public Fix105085AttrNSImpl(CoreDocumentImpl doc,
602: String namespaceUri, String qualifiedName) {
603: super (doc, namespaceUri, qualifiedName);
604: }
605:
606: public Fix105085AttrNSImpl(CoreDocumentImpl doc,
607: String namespaceUri, String qualifiedName,
608: String localPart) {
609: super (doc, namespaceUri, qualifiedName, localPart);
610: }
611:
612: @Override
613: public void setValue(String value) {
614: super .setValue(value);
615: // XXX #105085 Not storing the cached node, and forcing it to recreate each time.
616: textNode = null;
617: }
618: } // End of FixAttrNSImpl.
619:
620: private static void log(Exception ex) {
621: Logger logger = getLogger();
622: logger.log(Level.INFO, null, ex);
623: }
624:
625: private static Logger getLogger() {
626: return Logger.getLogger(CoreDocumentImpl.class.getName());
627: }
628: }
|