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.insync.faces;
042:
043: import java.beans.BeanInfo;
044: import java.beans.PropertyDescriptor;
045: import java.util.ArrayList;
046: import java.util.Iterator;
047:
048: import org.netbeans.modules.visualweb.extension.openide.util.Trace;
049: import org.w3c.dom.Attr;
050: import org.w3c.dom.Element;
051: import org.w3c.dom.NamedNodeMap;
052: import org.w3c.dom.Node;
053:
054: import com.sun.rave.designtime.Constants;
055: import com.sun.rave.designtime.Position;
056: import com.sun.rave.designtime.markup.AttributeDescriptor;
057: import com.sun.rave.designtime.markup.MarkupPosition;
058: import org.netbeans.modules.visualweb.insync.beans.Bean;
059: import org.netbeans.modules.visualweb.insync.beans.Property;
060:
061: /**
062: * Abstract superclass for a Bean that may have entries in markup source as well as Java source
063: *
064: * @author cquinn
065: */
066: public abstract class MarkupBean extends Bean {
067:
068: MarkupBean parent; // markup parent bean--may be set later, after constructor
069: final Element element; // underlying DOM JSP element
070: final ArrayList children; // optional list of children--null if this bean is not parent capable
071:
072: //--------------------------------------------------------------------------------- Construction
073:
074: /**
075: * Construct a bean bound to existing field & accessor methods, and page element. Parent will be
076: * set later using bindParent().
077: *
078: * @param unit
079: * @param beanInfo
080: * @param name
081: * @param field
082: * @param getter
083: * @param setter
084: */
085: public MarkupBean(FacesPageUnit unit, BeanInfo beanInfo,
086: String name, Element element) {
087: super (unit, beanInfo, name);
088: this .element = element;
089: children = isParentCapableBean(beanInfo) ? new ArrayList()
090: : null;
091:
092: // bind properties now since we have our element and it is easy to do here
093: NamedNodeMap attrmap = element.getAttributes();
094: for (int i = 0; i < attrmap.getLength(); i++) {
095: Attr attr = (Attr) attrmap.item(i);
096: PropertyDescriptor pd = getPropertyDescriptorForAttribute(attr
097: .getName());
098: if (pd != null && isMarkupProperty(pd)) {
099: MarkupProperty p = new MarkupProperty(this , pd,
100: element, attr);
101: properties.add(p);
102: }
103: }
104: }
105:
106: /**
107: * Construct a new bean, creating the underlying field and accessor methods and using given page
108: * element
109: *
110: * @param unit
111: * @param beanInfo
112: * @param name
113: * @param parent
114: */
115: public MarkupBean(FacesPageUnit unit, BeanInfo beanInfo,
116: String name, MarkupBean parent, Element element) {
117: super (unit, beanInfo, name);
118: this .parent = parent;
119: this .element = element;
120: children = isParentCapableBean(beanInfo) ? new ArrayList()
121: : null;
122: }
123:
124: /**
125: * @return true iff a bean defined by beanInfo is capable of having children.
126: */
127: private static boolean isParentCapableBean(BeanInfo beanInfo) {
128: Object ico = beanInfo.getBeanDescriptor().getValue(
129: "isContainer");
130: return !(ico instanceof Boolean)
131: || ((Boolean) ico).booleanValue();
132: }
133:
134: /**
135: * Remove this bean's field, methods and statements from the host class. This bean instance is
136: * dead & should not be used.
137: *
138: * @return true iff the source entry for this bean was actually removed.
139: */
140: public boolean removeEntry() {
141: Node parent = element.getParentNode();
142: assert Trace.trace("insync.faces", "FB removeEntry: " + this );
143: boolean removed = parent != null
144: && parent.removeChild(element) != null;
145: removed |= super .removeEntry();
146: return removed;
147: }
148:
149: //------------------------------------------------------------------------------------ Parenting
150:
151: /**
152: * @return the parent of this bean, null if top-level bean or dead
153: */
154: public Bean getParent() {
155: return parent;
156: }
157:
158: /**
159: * Take the opportinuty to scan for and bind to this bean's parent
160: *
161: * @return the parent of this bean iff not previously bound
162: */
163: public Bean bindParent() {
164: if (parent == null) {
165: // walk up element tree to find our parent
166: for (Node e = element.getParentNode(); e instanceof Element; e = e
167: .getParentNode()) {
168: parent = ((FacesPageUnit) unit)
169: .getMarkupBean((Element) e);
170: if (parent != null)
171: return parent;
172: }
173: }
174: return null;
175: }
176:
177: /**
178: * @return true iff this is capable of having children.
179: */
180: public boolean isParentCapable() {
181: return children != null;
182: }
183:
184: /*
185: * @see org.netbeans.modules.visualweb.insync.beans.Bean#addChild(org.netbeans.modules.visualweb.insync.beans.Bean, com.sun.rave.designtime.Position)
186: */
187: public void addChild(Bean child, Position pos) {
188: if (children != null && child instanceof MarkupBean) {
189: if (pos != null) {
190: // for markup positions mising the index, try to recompute it
191: if (pos instanceof MarkupPosition && pos.getIndex() < 0) {
192: Node before = ((MarkupPosition) pos)
193: .getBeforeSibling();
194: if (before != null) {
195: Element eparent = getElement();
196: Node n = eparent.getFirstChild();
197: for (int i = 0; i < children.size(); i++) {
198: Bean sib = (Bean) children.get(i);
199: if (sib instanceof MarkupBean) {
200: Element esib = ((MarkupBean) sib)
201: .getElement();
202: while (n != before && n != esib
203: && n != null)
204: n = n.getNextSibling();
205: if (n == before) {
206: pos.setIndex(i);
207: break;
208: }
209: }
210: }
211: }
212: }
213: // if we've got an index one way or another, use it
214: if (pos.getIndex() >= 0) {
215: children.add(pos.getIndex(), child);
216: return;
217: }
218: }
219: // plain old add at the end
220: children.add(child);
221: }
222: }
223:
224: /*
225: * @see org.netbeans.modules.visualweb.insync.beans.Bean#removeChild(org.netbeans.modules.visualweb.insync.beans.Bean)
226: */
227: public void removeChild(Bean child) {
228: if (children != null)
229: children.remove(child);
230: }
231:
232: /*
233: * @see org.netbeans.modules.visualweb.insync.beans.Bean#getChildren()
234: */
235: public Bean[] getChildren() {
236: return children != null ? (Bean[]) children
237: .toArray(EMPTY_ARRAY) : null;
238: }
239:
240: //------------------------------------------------------------------------------------ Accessors
241:
242: /**
243: * Set a given attribute identified by name to a given value, creating the attr as needed.
244: *
245: * @param name
246: * @param value
247: */
248: void setAttr(String name, String value) {
249: Attr attr = element.getAttributeNode(name);
250: if (attr == null)
251: element.setAttribute(name, value);
252: else if (!attr.getValue().equals(value))
253: attr.setValue(value);
254: }
255:
256: /**
257: * Returns a attribute identified by name.
258: *
259: * @param name
260: * @return value
261: */
262: public Attr getAttr(String name) {
263: return element.getAttributeNode(name);
264: }
265:
266: /**
267: * Remove a given attribute identified by name.
268: *
269: * @param name
270: */
271: void removeAttr(String name) {
272: Attr attr = element.getAttributeNode(name);
273: if (attr != null) {
274: element.removeAttribute(name);
275: }
276: }
277:
278: /*
279: * @see org.netbeans.modules.visualweb.insync.beans.Bean#getElement()
280: */
281: public Element getElement() {
282: return element;
283: }
284:
285: //----------------------------------------------------------------------------------- Properties
286:
287: /**
288: * Get the PropertyDescriptor for a property of this bean indicated by the attribute name
289: *
290: * @param attributeName the attribute name to look for
291: * @return the PropertyDescriptor for the property
292: */
293: public PropertyDescriptor getPropertyDescriptorForAttribute(
294: String attributeName) {
295: PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
296: for (int i = 0; i < pds.length; i++) {
297: PropertyDescriptor propertyDescriptor = pds[i];
298: AttributeDescriptor attributeDescriptor = (AttributeDescriptor) propertyDescriptor
299: .getValue(Constants.PropertyDescriptor.ATTRIBUTE_DESCRIPTOR);
300: if (attributeDescriptor != null
301: && attributeDescriptor.getName().equals(
302: attributeName)) {
303: return propertyDescriptor;
304: }
305: }
306: return null;
307: }
308:
309: /*
310: * @see org.netbeans.modules.visualweb.insync.beans.Bean#newCreatedProperty(java.beans.PropertyDescriptor)
311: */
312: protected Property newCreatedProperty(PropertyDescriptor pd) {
313: if (isMarkupProperty(pd))
314: return new MarkupProperty(this , pd, element);
315: else
316: return super .newCreatedProperty(pd);
317: }
318:
319: /*
320: * @see org.netbeans.modules.visualweb.insync.beans.Bean#isMarkupProperty(java.beans.PropertyDescriptor)
321: */
322: public boolean isMarkupProperty(PropertyDescriptor pd) {
323: return true;
324: }
325:
326: //-------------------------------------------------------------------------------------- Object
327:
328: /*
329: * @see org.netbeans.modules.visualweb.insync.beans.BeansNode#toString(java.lang.StringBuffer)
330: */
331: public void toString(StringBuffer sb) {
332: //sb.append(" prnt:");
333: //sb.append(parent);
334: sb.append(" elem:");
335: sb.append(element);
336: super.toString(sb);
337: }
338:
339: }
|