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.jsf;
043:
044: import com.sun.rave.designtime.DesignBean;
045: import com.sun.rave.designtime.DesignContext;
046: import com.sun.rave.designtime.markup.MarkupDesignBean;
047: import java.awt.Point;
048: import java.util.logging.Logger;
049: import org.netbeans.modules.visualweb.api.designer.Designer;
050: import org.netbeans.modules.visualweb.api.designer.Designer.Box;
051: import org.netbeans.modules.visualweb.api.designer.cssengine.StyleData;
052: import org.netbeans.modules.visualweb.api.designtime.idebridge.DesigntimeIdeBridgeProvider;
053: import org.netbeans.modules.visualweb.designer.html.HtmlTag;
054: import org.netbeans.modules.visualweb.designer.jsf.ui.JsfMultiViewElement;
055: import org.netbeans.modules.visualweb.insync.Util;
056: import org.netbeans.modules.visualweb.insync.live.LiveUnit;
057: import org.netbeans.modules.visualweb.insync.markup.MarkupUnit;
058: import org.netbeans.modules.visualweb.insync.models.FacesModel;
059: import org.openide.ErrorManager;
060: import org.openide.filesystems.FileObject;
061: import org.openide.loaders.DataObject;
062: import org.w3c.dom.Document;
063: import org.w3c.dom.DocumentFragment;
064: import org.w3c.dom.Element;
065: import org.w3c.dom.Node;
066: import org.w3c.dom.NodeList;
067:
068: /**
069: * Utilities class for the JSF support module.
070: *
071: * @author Peter Zavadsky
072: */
073: public final class JsfSupportUtilities {
074:
075: /** Creates a new instance of JsfSupportUtilities */
076: private JsfSupportUtilities() {
077: }
078:
079: public static String[] getEditableProperties(DesignBean designBean) {
080: return DomProviderServiceImpl
081: .getEditablePropertyNames(designBean);
082: }
083:
084: public static Designer findDesignerForDesignContext(
085: DesignContext designContext) {
086: Designer[] designers = JsfForm
087: .findDesignersForDesignContext(designContext);
088: return designers.length == 0 ? null : designers[0];
089: }
090:
091: public static Designer findDesignerForJsfForm(JsfForm jsfForm) {
092: Designer[] designers = JsfForm.findDesigners(jsfForm);
093: return designers.length == 0 ? null : designers[0];
094: }
095:
096: public static Element getComponentRootElementForDesignBean(
097: DesignBean designBean) {
098: if (designBean instanceof MarkupDesignBean) {
099: return DomProviderImpl
100: .getComponentRootElementForMarkupDesignBean((MarkupDesignBean) designBean);
101: }
102: return null;
103: }
104:
105: public static JsfForm findJsfFormForDesignContext(
106: DesignContext designContext) {
107: return JsfForm.findJsfForm(designContext);
108: }
109:
110: // XXX Also in designer/../DesignerUtils.
111: /** Return true iff the string contains only whitespace */
112: public static boolean onlyWhitespace(String s) {
113: // if(DEBUG) {
114: // debugLog(DesignerUtils.class.getName() + ".onlyWhitespace(String)");
115: // }
116: if (s == null) {
117: return true;
118: }
119: int n = s.length();
120:
121: for (int i = 0; i < n; i++) {
122: char c = s.charAt(i);
123:
124: /* See the "empty-cells" documentation in CSS2.1 for example:
125: * it sounds like only SOME of the whitespace characters are
126: * truly considered ignorable whitespace: \r, \n, \t, and space.
127: * So do something more clever in some of these cases.
128: */
129: if (!Character.isWhitespace(c)) {
130: return false;
131: }
132: }
133:
134: return true;
135: }
136:
137: public static boolean isSpecialComponent(
138: Element componentRootElement) {
139: MarkupDesignBean markupDesignBean = MarkupUnit
140: .getMarkupDesignBeanForElement(componentRootElement);
141: if (markupDesignBean == null) {
142: return false;
143: }
144: return Util.isSpecialBean(markupDesignBean);
145: }
146:
147: public static boolean isWebFormDataObject(DataObject dataObject) {
148: return dataObject != null
149: && isWebFormFileObject(dataObject.getPrimaryFile());
150: }
151:
152: public static boolean isWebFormFileObject(FileObject fileObject) {
153: return fileObject != null
154: && FacesModel.getInstance(fileObject) != null;
155: }
156:
157: public static void updateLocalStyleValuesForElement(Element e,
158: StyleData[] setStyleData, StyleData[] removeStyleData) {
159: Util.updateLocalStyleValuesForElement(e, setStyleData,
160: removeStyleData);
161: }
162:
163: public static boolean setStyleAttribute(
164: Element componentRootElement, String attribute, int value) {
165: MarkupDesignBean markupDesignBean = MarkupUnit
166: .getMarkupDesignBeanForElement(componentRootElement);
167: if (markupDesignBean == null) {
168: return false;
169: }
170: return Util.setDesignProperty(markupDesignBean, attribute,
171: value);
172: }
173:
174: public static Element getParentComponent(
175: Element componentRootElement) {
176: MarkupDesignBean markupDesignBean = MarkupUnit
177: .getMarkupDesignBeanForElement(componentRootElement);
178: if (markupDesignBean == null) {
179: return null;
180: }
181:
182: DesignBean parent = markupDesignBean.getBeanParent();
183: return parent instanceof MarkupDesignBean ? getComponentRootElementForMarkupDesignBean((MarkupDesignBean) parent)
184: : null;
185: }
186:
187: public/*private*/static Element getComponentRootElementForMarkupDesignBean(
188: MarkupDesignBean markupDesignBean) {
189: return getComponentRootElementForDesignBean(markupDesignBean);
190: }
191:
192: public static Element getComponentRootElementForElement(
193: Element element) {
194: return getComponentRootElementForDesignBean(MarkupUnit
195: .getMarkupDesignBeanForElement(element));
196: }
197:
198: public static Element getComponentRootElementFromNode(
199: org.openide.nodes.Node node) {
200: DesignBean bean = (DesignBean) node.getLookup().lookup(
201: DesignBean.class);
202: if (bean == null) {
203: ErrorManager.getDefault().notify(
204: ErrorManager.INFORMATIONAL,
205: new NullPointerException("No DesignBean for node="
206: + node)); // NOI18N
207: return null;
208: }
209:
210: return bean instanceof MarkupDesignBean ? getComponentRootElementForMarkupDesignBean((MarkupDesignBean) bean)
211: : null;
212: }
213:
214: public static Element findHtmlElementDescendant(DocumentFragment df) {
215: return Util.findDescendant(HtmlTag.HTML.name, df);
216: }
217:
218: public static Element findHtmlElementDescendant(Document doc) {
219: if (doc == null) {
220: return null;
221: }
222: return Util.findDescendant(HtmlTag.HTML.name, doc
223: .getDocumentElement());
224: }
225:
226: // XXX Copy also in designer/../GridHandler.
227: /** Given absolute coordinates x,y in the viewport, compute
228: * the CSS coordinates to assign to a box if it's parented by
229: * the given parentBox such that the coordinates will result
230: * in a box showing up at the absolute coordinates.
231: * That was a really convoluted explanation, so to be specific:
232: * If you have an absolutely positioned <div> at 100, 100,
233: * and you drag a button into it such that it's its child,
234: * and you drag it to screen coordinate 75, 150, then, in order
235: * for the button to be rendered at 75, 150 and be a child of
236: * the div its top/left coordinates must be -25, 50.
237: */
238: public static Point translateCoordinates(Box parentBox, int x, int y) {
239: while (parentBox != null) {
240: // if (parentBox.getBoxType().isPositioned()) {
241: if (parentBox.isPositioned()) {
242: x -= parentBox.getAbsoluteX();
243: y -= parentBox.getAbsoluteY();
244:
245: return new Point(x, y);
246: }
247:
248: if (parentBox.getPositionedBy() != null) {
249: parentBox = parentBox.getPositionedBy();
250: } else {
251: parentBox = parentBox.getParent();
252: }
253: }
254:
255: return new Point(x, y);
256: }
257:
258: public static void tcRepaint(Designer designer) {
259: JsfMultiViewElement jsfMultiViewElement = JsfForm
260: .findJsfMultiViewElementForDesigner(designer);
261: if (jsfMultiViewElement == null) {
262: return;
263: }
264: jsfMultiViewElement.getJsfTopComponent().repaint();
265: }
266:
267: public static org.openide.nodes.Node getNodeRepresentation(
268: Element componentRootElement) {
269: MarkupDesignBean markupDesignBean = MarkupUnit
270: .getMarkupDesignBeanForElement(componentRootElement);
271: return DesigntimeIdeBridgeProvider.getDefault()
272: .getNodeRepresentation(markupDesignBean);
273: }
274:
275: /**
276: * Return the given node corresponding to the given xpath.
277: * NOTE: The xpath parameter may actually contain multiple xpaths
278: * separated by colons.
279: * NOTE: Only a simple subset of XPATH is supported/implemented!!
280: * I support EXACTLY the following formats:
281: * //tagname
282: * //tagname[@attribute='value']
283: * and
284: * /tagname1/tagname2/.../tagnameN
285: * /tagname1/tagname2/.../tagnameN[@attribute='value']
286: * Note - combinations of these (e.g. //foo/bar[@baz='boo']/nei are not valid yet).
287: *
288: * @todo Hook up to xalan or other XPATH parser to get this working properly
289: */
290: public static Node findPropertyNode(Node root, String xpaths) {
291: int next = 0;
292: int xpathsLength = xpaths.length();
293:
294: while (next <= xpathsLength) {
295: String xpath;
296: int xpathEnd = xpaths.indexOf(':', next);
297:
298: if (xpathEnd == -1) {
299: xpath = xpaths.substring(next);
300: next = xpathsLength + 1;
301: } else {
302: xpath = xpaths.substring(next, xpathEnd);
303: next = xpathEnd + 1;
304: }
305:
306: // Dumb/simple parser algorithm for now
307: if (xpath.startsWith("//")) { // NOI18N
308:
309: int length = xpath.length();
310: int begin = 2;
311: int end = begin;
312:
313: while ((end < length)
314: && Character.isLetter(xpath.charAt(end))) {
315: end++;
316: }
317:
318: String attributeName = null;
319: String attributeValue = null;
320: String tagName = xpath.substring(begin, end);
321:
322: if ((end < length) && xpath.startsWith("[@", end)) { // NOI18N
323: begin = end + 2;
324: end = begin;
325:
326: while ((end < length)
327: && Character.isLetter(xpath.charAt(end))) {
328: end++;
329: }
330:
331: attributeName = xpath.substring(begin, end);
332:
333: if ((end < length) && xpath.startsWith("='", end)) { // NOI18N
334: begin = end + 2;
335: end = begin;
336:
337: while ((end < length)
338: && (xpath.charAt(end) != '\'')) {
339: end++;
340: }
341:
342: attributeValue = xpath.substring(begin, end);
343: end++;
344: }
345: }
346:
347: // if (end != length) {
348: // // Looks like the xpath expession is not of the simple form used
349: // // for most of our own components... so do a fullblown
350: // // xpath parse looking for the node instead...
351: // // TODO
352: // }
353: Element element = findElement(root, tagName,
354: attributeName, attributeValue);
355:
356: if (element != null) {
357: return element;
358: }
359: } else {
360: info("Inline editing xpath expression not understood: "
361: + xpath); // NOI18N
362: }
363: }
364:
365: return null;
366: }
367:
368: private static Element findElement(Node node, String tagName,
369: String attribute, String value) {
370: if (node.getNodeType() == Node.ELEMENT_NODE) {
371: Element element = (Element) node;
372:
373: if (element.getTagName().equals(tagName)) {
374: if (attribute != null) {
375: if ((value == null)
376: && element.hasAttribute(attribute)) {
377: return element;
378: } else if (element.getAttribute(attribute).indexOf(
379: value) != -1) {
380: //} else if (element.getAttribute(attribute).equals(value)) {
381: // Match substring, not =: appropriate for class attribute only
382: // PENDING: What is the correct xpath to express
383: // element e has a class attribute which INCLUDES substring foo?
384: return element;
385: }
386: } else {
387: return element;
388: }
389: }
390: }
391:
392: NodeList children = node.getChildNodes();
393:
394: for (int i = 0, n = children.getLength(); i < n; i++) {
395: Node child = children.item(i);
396: Element element = findElement(child, tagName, attribute,
397: value);
398:
399: if (element != null) {
400: return element;
401: }
402: }
403:
404: return null;
405: }
406:
407: public static boolean isTrayComponent(Element componentRootElement) {
408: MarkupDesignBean markupDesignBean = MarkupUnit
409: .getMarkupDesignBeanForElement(componentRootElement);
410: if (markupDesignBean == null) {
411: return false;
412: }
413: return LiveUnit.isTrayBean(markupDesignBean);
414: }
415:
416: private static Logger getLogger() {
417: return Logger.getLogger(JsfSupportUtilities.class.getName());
418: }
419:
420: private static void info(String message) {
421: getLogger().info(message);
422: }
423: }
|