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:
042: package org.netbeans.modules.visualweb.designer.markup;
043:
044: import org.netbeans.modules.visualweb.api.insync.InSyncService;
045: import java.io.File;
046: import java.net.MalformedURLException;
047: import java.net.URL;
048: import org.w3c.dom.Attr;
049: import org.w3c.dom.Document;
050: import org.w3c.dom.Element;
051: import org.w3c.dom.Node;
052:
053: /**
054: * Utilities for markup packages.
055: *
056: * Delegated to MarkupService.
057: *
058: * @author Peter Zavadsky
059: */
060: final class MarkupUtilities {
061:
062: // From org.apache.batik.util.XMLConstants:
063: // XXX Then from org.netbeans.modules.visualweb.insync.Util.
064: /**
065: * The XML namespace URI.
066: */
067: private static String XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace"; // NOI18N
068:
069: private MarkupUtilities() {
070: }
071:
072: /**
073: * Returns the xml:base attribute value of the given element
074: * Resolving any dependency on parent bases if needed.
075: */
076: // THIS METHOD COPIED FROM org.apache.batik.dom.svg.XMLBaseSupport;
077: // that code is in a separate batik jar I don't want to include (batik-dom).
078: // I changed it to return a URL instead of converting to String, then back
079: // to URL in the client below.
080: //
081: // THE LICENSE IS OBVIOUSLY THAT OF THE BATIK DISTRIBUTION, NOT THE
082: // REST OF THE FILE.
083: // XXX Then from org.netbeans.modules.visualweb.insync.Util.
084: public static URL getCascadedXMLBase(Element elt) {
085: URL base = null;
086: Node n = elt.getParentNode();
087: while (n != null) {
088: if (n.getNodeType() == Node.ELEMENT_NODE) {
089: base = getCascadedXMLBase((Element) n);
090: break;
091: }
092: /*
093: if (n instanceof CSSImportedElementRoot) {
094: n = ((CSSImportedElementRoot)n).getCSSParentElement();
095: } else {
096: */
097: n = n.getParentNode();
098: /*
099: }
100: */
101: }
102: if (base == null) {
103: // RaveDocument xhtmlDoc = (RaveDocument)elt.getOwnerDocument();
104: // URL url = xhtmlDoc.getUrl();
105: Document document = elt.getOwnerDocument();
106: URL url = InSyncService.getProvider().getUrl(document);
107: if (url != null) {
108: return url;
109: }
110: }
111: Attr attr = elt.getAttributeNodeNS(XML_NAMESPACE_URI, "base");
112: if (attr != null) {
113: try {
114: if (base == null) {
115: base = new URL(attr.getNodeValue());
116: } else {
117: base = new URL(base, attr.getNodeValue());
118: }
119: } catch (MalformedURLException ue) {
120: // org.openide.ErrorManager.getDefault().notify(ue);
121: ue.printStackTrace();
122: }
123: }
124: return base;
125: }
126:
127: // // XXX From org.netbeans.modules.visualweb.insync.Util.
128: // /**
129: // * Given an element which may be in a rendered DocumentFragment, return the corresponding JSF
130: // * element in the source.
131: // */
132: // public static Element getCorrespondingSourceElement(Element elem) {
133: // if (!(elem instanceof RaveElement)) {
134: // return elem;
135: // }
136: //
137: // RaveElement element = (RaveElement)elem;
138: //
139: // if (!element.isRendered()) {
140: // return element;
141: // }
142: //
143: // Node node = element;
144: // while (node != null) {
145: // if (node instanceof RaveElement) {
146: // RaveElement xel = (RaveElement)node;
147: // if (xel.isRendered()) {
148: // RaveElement src = xel.getSource();
149: // if (src != null) {
150: // return src;
151: // }
152: // }
153: // }
154: // node = node.getParentNode();
155: // }
156: // return element.getSourceElement();
157: // }
158: //
159: //
160: // // <markup_separation> copied from insync/Util
161: // // XXX This should be separate utility api, openide extension or what.
162: // /**
163: // * Show the given line in a particular file.
164: // *
165: // * @param filename The full path to the file
166: // * @param lineno The line number
167: // * @param openFirst Usually you'll want to pass false. When set to true, this will first open
168: // * the file, then request the given line number; this works around certain bugs for
169: // * some editor types like CSS files.
170: // */
171: // private static void show(String filename, int lineno, int column, boolean openFirst) {
172: // File file = new File(filename);
173: // FileObject fo = FileUtil.toFileObject(file);
174: // if (fo != null) {
175: // show(fo, lineno, column, openFirst);
176: // }
177: // }
178: //
179: // /**
180: // * @param fo
181: // * @param lineno
182: // * @param column
183: // * @param openFirst
184: // * @return
185: // */
186: // private static void showLineAt(FileObject fo, int lineno, int column, boolean openFirst) {
187: // DataObject dobj;
188: // try {
189: // dobj = DataObject.find(fo);
190: // }
191: // catch (DataObjectNotFoundException ex) {
192: // ErrorManager.getDefault().notify(ex);
193: // return;
194: // }
195: //
196: // // Try to open doc before showing the line. This SHOULD not be
197: // // necessary, except without this the IDE hangs in its attempt
198: // // to open the file when the file in question is a CSS file.
199: // // Probably a bug in the xml/css module's editorsupport code.
200: // // This has the negative effect of first flashing the top
201: // // of the file before showing the destination line, so
202: // // this operation is made conditional so only clients who
203: // // actually need it need to use it.
204: // if (openFirst) {
205: // EditorCookie ec = (EditorCookie)dobj.getCookie(EditorCookie.class);
206: // if (ec != null) {
207: // try {
208: // ec.openDocument(); // ensure that it has been opened - REDUNDANT?
209: // //ec.open();
210: // }
211: // catch (IOException ex) {
212: // ErrorManager.getDefault().notify(ex);
213: // }
214: // }
215: // }
216: //
217: // LineCookie lc = (LineCookie)dobj.getCookie(LineCookie.class);
218: // if (lc != null) {
219: // Line.Set ls = lc.getLineSet();
220: // if (ls != null) {
221: // // -1: convert line numbers to be zero-based
222: // Line line = ls.getCurrent(lineno-1);
223: // // TODO - pass in a column too?
224: // line.show(Line.SHOW_GOTO, column);
225: // }
226: // }
227: // }
228: // // <markup_separation> copied form insync/Util
229: //
230: // // <markup_separation> moved from insync/MarkupUnit
231: // /** Convert the given URL to a path: decode spaces from %20's, etc.
232: // * If the url does not begin with "file:" it will not do anything.
233: // * @todo Find a better home for this method
234: // */
235: // public static String fromURL(String url) {
236: // if (url.startsWith("file:")) { // NOI18N
237: // int n = url.length();
238: // StringBuffer sb = new StringBuffer(n);
239: // for (int i = 5; i < n; i++) {
240: // char c = url.charAt(i);
241: // // TODO -- any File.separatorChar manipulation perhaps?
242: // if (c == '%' && i < n-3) {
243: // char d1 = url.charAt(i+1);
244: // char d2 = url.charAt(i+2);
245: // if (Character.isDigit(d1) && Character.isDigit(d2)) {
246: // String numString = ""+d1+d2;
247: // try {
248: // int num = Integer.parseInt(numString, 16);
249: // if (num >= 0 && num <= 255) {
250: // sb.append((char)num);
251: // i += 2;
252: // continue;
253: // }
254: // } catch (NumberFormatException nex) {
255: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, nex);
256: // }
257: // }
258: // sb.append(c);
259: // } else {
260: // sb.append(c);
261: // }
262: // }
263: // return sb.toString();
264: // }
265: // return url;
266: // }
267: // // </markup_separation>
268:
269: // <error_handling> Moved from RaveDocument.
270: // XXX These methods are suspicoius, they deal with openide output window.
271: // // and there may not be any knowing about it from this impls.
272: // /** Clear document related errors.
273: // * @param delayed When set, don't actually clear the errors right now;
274: // * it clears the errors next time another error is added. */
275: // public static void clearErrors(boolean delayed) {
276: // if (delayed) {
277: // clearErrors = true;
278: // } else {
279: // OutputWriter out = getOutputWriter();
280: // try {
281: // out.reset();
282: // }
283: // catch (IOException ioe) {
284: // // This is lame - our own output window shouldn't
285: // // throw IO exceptions!
286: // ErrorManager.getDefault().notify(ioe);
287: // }
288: // }
289: // }
290: //
291: // private static boolean clearErrors = false;
292: //
293: // private static OutputWriter getOutputWriter() {
294: // InputOutput io = IOProvider.getDefault().getIO(NbBundle.getMessage(MarkupUtilities.class, "WindowTitle"), false);
295: // OutputWriter out = io.getOut();
296: // return out;
297: // }
298: //
299: // /**
300: // * Cause the panel/window within which errors are displayed to come to the front if possible.
301: // *
302: // */
303: // public static void selectErrors() {
304: // InputOutput io = IOProvider.getDefault().getIO(NbBundle.getMessage(MarkupUtilities.class, "WindowTitle"), false);
305: // io.select();
306: // }
307: //
308: // public static void displayError(String message) {
309: // displayErrorForFileObject(message, null, -1, -1);
310: // }
311: //
312: // public static void displayErrorForLocation(String message, Object location, int line, int column) {
313: // String fileName = computeFilename(location);
314: // line = computeLineNumber(location, line);
315: //
316: // File file = new File(fileName);
317: // FileObject fo = FileUtil.toFileObject(file);
318: //
319: // displayErrorForFileObject(message, fo, line >= 1 ? line : 1, column);
320: // }
321: //
322: // /** Given a general location object provided from the CSS parser,
323: // * compute the correct file name to use.
324: // */
325: // static String computeFilename(Object location) {
326: // if (location instanceof String) {
327: // return (String)location;
328: // } else if (location instanceof URL) {
329: // // <markup_separation>
330: //// return MarkupUnit.fromURL(((URL)location).toExternalForm());
331: // // ====
332: // return MarkupService.fromURL(((URL)location).toExternalForm());
333: // // </markup_separation>
334: // } else if (location instanceof Element) {
335: // // Locate the filename for a given element
336: // Element element = (Element)location;
337: // element = MarkupService.getCorrespondingSourceElement(element);
338: //
339: // // <markup_separation>
340: //// // XXX I should derive this from the engine instead, after all
341: //// // the engine can know the unit! (Since engines cannot be used
342: //// // with multiple DOMs anyway)
343: //// FileObject fo = unit.getFileObject();
344: // // ====
345: // FileObject fo = InSyncService.getProvider().getFileObject(element.getOwnerDocument());
346: // // </markup_separation>
347: // File f = FileUtil.toFile(fo);
348: //
349: // return f.toString();
350: // } else if (location != null) {
351: // return location.toString();
352: // }
353: //
354: // return "";
355: // }
356: //
357: // static int computeLineNumber(Object location, int lineno) {
358: // if (location instanceof Element) {
359: // /*
360: // // The location is an XhtmlElement -- so the line number
361: // // needs to be relative to it.... compute the line number
362: // // of the element
363: // if (lineno == -1)
364: // lineno = 0;
365: // Element element = (Element)location;
366: // RaveDocument doc = (RaveDocument)element.getOwnerDocument();
367: // lineno += doc.getLineNumber(element);
368: // */
369: // if (lineno == -1) {
370: // lineno = 0;
371: // }
372: //
373: // Element element = (Element)location;
374: // element = MarkupService.getCorrespondingSourceElement(element);
375: // // <markup_separation>
376: //// lineno += unit.computeLine(element);
377: // // ====
378: // lineno += InSyncService.getProvider().computeLine(element.getOwnerDocument(), element);
379: // // </markup_separation>
380: // }
381: //
382: // return lineno;
383: // }
384: //
385: // public static void displayErrorForFileObject(String message, final FileObject fileObject, final int line, final int column){
386: //// final XhtmlElement e = Util.getSource(element);
387: // OutputListener listener;
388: // if (fileObject == null) {
389: // listener = null;
390: // } else {
391: // listener = new OutputListener() {
392: // public void outputLineSelected(OutputEvent ev) {
393: // }
394: // public void outputLineAction(OutputEvent ev) {
395: //// Util.show(null, unit.getFileObject(), unit.getLine(e),
396: //// 0, true);
397: // // <markup_separation>
398: //// Util.show(null, fileObject, lineNumber, 0, true);
399: // // ====
400: // showLineAt(fileObject, line, column, true);
401: // // </markup_separation>
402: // }
403: // public void outputLineCleared (OutputEvent ev) {
404: // }
405: // };
406: // }
407: //
408: // displayError(message, listener);
409: // }
410: //
411: // /**
412: // * Display the given error message to the user. The optional listener argument
413: // * (pass in null if not applicable) will make the line hyperlinked and the
414: // * listener is invoked to process any user clicks.
415: // * @param message The string to be displayed to the user
416: // * @param listener null, or a listener to be notified when the user clicks
417: // * the linked message
418: // */
419: // private static void displayError(String message, OutputListener listener) {
420: // OutputWriter out = getOutputWriter();
421: // try {
422: // if (clearErrors) {
423: // out.reset();
424: // clearErrors = false;
425: // }
426: // // Write the error message to the output tab:
427: // out.println(message, listener);
428: // }
429: // catch (IOException ioe) {
430: // // This is lame - our own output window shouldn't throw IO exceptions!
431: // ErrorManager.getDefault().notify(ioe);
432: // }
433: // }
434: // </error_handling>
435:
436: // // XXX Moved from DesignerService.
437: // /**
438: // * Return an InputStream for the given CSS URI, if the corresponding CSS
439: // * file is open and edited. Otherwise return null.
440: // *
441: // * @param uri The URI to the CSS file. <b>MUST</b> be an absolute file url!
442: // * @return An InputStream for the live edited CSS
443: // */
444: // public static InputStream getOpenCssStream(String uriString) {
445: // try {
446: // URI uri = new URI(uriString);
447: // File file = new File(uri);
448: //
449: // if (file != null) {
450: // FileObject fobj = FileUtil.toFileObject(file);
451: //
452: // if (fobj != null) {
453: // try {
454: // DataObject dobj = DataObject.find(fobj);
455: // EditorCookie ec = (EditorCookie)dobj.getCookie(EditorCookie.class);
456: //
457: // if (ec != null) {
458: // javax.swing.text.Document doc = ec.getDocument();
459: //
460: // if (doc != null) {
461: // // XXX Isn't there a better way to return an input stream
462: // // for a String? Should I have my own?
463: // String s = doc.getText(0, doc.getLength());
464: //
465: // return new StringBufferInputStream(s);
466: // }
467: // }
468: // } catch (BadLocationException ble) {
469: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ble);
470: // } catch (DataObjectNotFoundException dnfe) {
471: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, dnfe);
472: // }
473: // }
474: // }
475: // } catch (URISyntaxException e) {
476: // ErrorManager.getDefault().notify(e);
477: // }
478: //
479: // return null;
480: // }
481: }
|