KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > lobobrowser > html > domimpl > HTMLElementImpl


1 /* GNU LESSER GENERAL PUBLIC LICENSE
2     Copyright (C) 2006 The Lobo Project
3
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Lesser General Public
6     License as published by the Free Software Foundation; either
7     version 2.1 of the License, or (at your option) any later version.
8
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12     Lesser General Public License for more details.
13
14     You should have received a copy of the GNU Lesser General Public
15     License along with this library; if not, write to the Free Software
16     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
18     Contact info: xamjadmin@users.sourceforge.net
19 */

20 /*
21  * Created on Sep 3, 2005
22  */

23 package org.lobobrowser.html.domimpl;
24
25 import org.lobobrowser.html.FormInput;
26 import org.lobobrowser.html.parser.HtmlParser;
27 import org.lobobrowser.html.style.*;
28 import org.lobobrowser.util.*;
29 import org.w3c.css.sac.InputSource;
30 import org.w3c.dom.*;
31 import org.w3c.dom.css.CSSStyleDeclaration;
32 import org.w3c.dom.html2.*;
33 import com.steadystate.css.parser.CSSOMParser;
34
35 import java.io.*;
36 import java.util.ArrayList JavaDoc;
37 import java.util.Collection JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.Map JavaDoc;
40 import java.util.StringTokenizer JavaDoc;
41 import java.util.logging.*;
42
43 public class HTMLElementImpl extends ElementImpl implements HTMLElement, CSS2PropertiesContext {
44     private final boolean noStyleSheet;
45     
46     public HTMLElementImpl(String JavaDoc name, boolean noStyleSheet) {
47         super(name);
48         this.noStyleSheet = noStyleSheet;
49     }
50     
51     public HTMLElementImpl(String JavaDoc name) {
52         super(name);
53         this.noStyleSheet = false;
54     }
55     
56     private volatile CSS2PropertiesImpl currentStyleDeclarationState;
57     private volatile CSS2PropertiesImpl localStyleDeclarationState;
58
59     protected final void forgetLocalStyle() {
60         synchronized(this) {
61             this.currentStyleDeclarationState = null;
62             this.localStyleDeclarationState = null;
63         }
64     }
65
66     protected final void forgetStyle(boolean deep) {
67         //TODO: Maybe this should redo style declarations in
68
//CSS, not the whole properties object?
69
synchronized(this) {
70             this.currentStyleDeclarationState = null;
71             if(deep) {
72                 java.util.ArrayList JavaDoc nl = this.nodeList;
73                 if(nl != null) {
74                     Iterator JavaDoc i = nl.iterator();
75                     while(i.hasNext()) {
76                         Object JavaDoc node = i.next();
77                         if(node instanceof HTMLElementImpl) {
78                             ((HTMLElementImpl) node).forgetStyle(deep);
79                         }
80                     }
81                 }
82             }
83         }
84     }
85
86     /**
87      * Gets the style object associated with the element.
88      * It may return null only if the type of element does not handle stylesheets.
89      */

90     public CSS2PropertiesImpl getCurrentStyle() {
91         CSS2PropertiesImpl sds;
92         synchronized(this) {
93             sds = this.currentStyleDeclarationState;
94             if(sds != null) {
95                 return sds;
96             }
97         }
98         // Can't do the following in synchronized block (reverse locking order with document).
99
// First, add declarations from stylesheet
100
sds = this.addStyleSheetDeclarations(sds);
101         // Now add local style if any.
102
CSS2PropertiesImpl localStyle = this.getStyle();
103         if(sds == null) {
104             sds = new CSS2PropertiesImpl(this);
105             sds.setLocalStyleProperties(localStyle);
106         }
107         else {
108             sds.setLocalStyleProperties(localStyle);
109         }
110         synchronized(this) {
111             // Check if style properties were set while outside
112
// the synchronized block (can happen).
113
CSS2PropertiesImpl setProps = this.currentStyleDeclarationState;
114             if(setProps != null) {
115                 return setProps;
116             }
117             this.currentStyleDeclarationState = sds;
118             return sds;
119         }
120     }
121     
122 // /**
123
// * This method may return <code>null</code> if
124
// * there's no style declaration applicable to
125
// * the element.
126
// */
127
// public CSS2PropertiesImpl getCurrentStyle() {
128
// if(this.noStyleSheet) {
129
// return null;
130
// }
131
// synchronized(this) {
132
// Object sdsObj = this.styleDeclarationNoCreate;
133
// if(sdsObj == INVALID_CSS) {
134
// CSS2PropertiesImpl sdsOrig = this.styleDeclarationState;
135
// if(sdsOrig != null) {
136
// this.styleDeclarationNoCreate = sdsOrig;
137
// return sdsOrig;
138
// }
139
// }
140
// else {
141
// return (CSS2PropertiesImpl) sdsObj;
142
// }
143
// }
144
// // Cannot run the following in synchronized block (reverse lock order with document).
145
// // First, add declarations from stylesheet
146
// CSS2PropertiesImpl sds = this.addStyleSheetDeclarations(null);
147
// // Now add local style if any
148
// CSS2PropertiesImpl localStyle = this.getStyleNoCreate();
149
// if(localStyle != null) {
150
// if(sds == null) {
151
// sds = new CSS2PropertiesImpl(this);
152
// }
153
// sds.setLocalStyleProperties(localStyle);
154
// }
155
// synchronized(this) {
156
// this.styleDeclarationNoCreate = sds;
157
// }
158
// return sds;
159
// }
160

161     /**
162      * Gets the local style object associated with the element. The properties
163      * object returned only includes properties from the local style attribute.
164      * It may return null only if the type of element does not handle stylesheets.
165      */

166     public CSS2PropertiesImpl getStyle() {
167         CSS2PropertiesImpl sds;
168         synchronized(this) {
169             sds = this.localStyleDeclarationState;
170             if(sds != null) {
171                 return sds;
172             }
173             sds = new CSS2PropertiesImpl(this);
174             // Add any declarations in style attribute (last takes precedence).
175
String JavaDoc style = this.getAttribute("style");
176             if(style != null && style.length() != 0) {
177                 CSSOMParser parser = new CSSOMParser();
178                 InputSource inputSource = this.getCssInputSourceForDecl(style);
179                 try {
180                     CSSStyleDeclaration sd = parser.parseStyleDeclaration(inputSource);
181                     sds.addStyleDeclaration(sd);
182                 } catch(Exception JavaDoc err) {
183                     String JavaDoc id = this.getId();
184                     String JavaDoc withId = id == null ? "" : " with ID '" + id + "'";
185                     this.warn("Unable to parse style attribute value for element " + this.getTagName() + withId + ".", err);
186                 }
187             }
188             this.localStyleDeclarationState = sds;
189         }
190         // Synchronization note: Make sure getStyle() does not return multiple values.
191
return sds;
192     }
193     
194 // /**
195
// * This method may return <code>null</code> if
196
// * there's no style declaration applicable to
197
// * the element.
198
// */
199
// public CSS2PropertiesImpl getStyleNoCreate() {
200
// if(this.noStyleSheet) {
201
// return null;
202
// }
203
// synchronized(this) {
204
// Object sdsObj = this.localStyleDeclarationNoCreate;
205
// if(sdsObj == INVALID_CSS) {
206
// CSS2PropertiesImpl sdsOrig = this.localStyleDeclarationState;
207
// if(sdsOrig != null) {
208
// this.localStyleDeclarationNoCreate = sdsOrig;
209
// return sdsOrig;
210
// }
211
// // Otherwise, fall through.
212
// }
213
// else {
214
// return (CSS2PropertiesImpl) sdsObj;
215
// }
216
// }
217
// // Then add any declarations in style attribute (last takes precedence).
218
// CSS2PropertiesImpl sds = null;
219
// String style = this.getAttribute("style");
220
// if(style != null && style.length() != 0) {
221
// CSSOMParser parser = new CSSOMParser();
222
// InputSource inputSource = this.getCssInputSourceForDecl(style);
223
// try {
224
// CSSStyleDeclaration sd = parser.parseStyleDeclaration(inputSource);
225
// if(sds == null) {
226
// sds = new CSS2PropertiesImpl(this);
227
// }
228
// sds.addStyleDeclaration(sd);
229
// } catch(Exception err) {
230
// String id = this.getId();
231
// String withId = id == null ? "" : " with ID '" + id + "'";
232
// this.warn("Unable to parse style attribute value for element " + this.getTagName() + withId + ".", err);
233
// }
234
// }
235
// synchronized(this) {
236
// this.localStyleDeclarationNoCreate = sds;
237
// if(sds != null) {
238
// Object gsp = this.styleDeclarationNoCreate;
239
// if(gsp == null && sds != null) {
240
// this.styleDeclarationNoCreate = INVALID_CSS;
241
// this.styleDeclarationState = null;
242
// }
243
// else if(gsp != INVALID_CSS) {
244
// ((CSS2PropertiesImpl) gsp).setLocalStyleProperties(sds);
245
// }
246
// }
247
// }
248
// return sds;
249
// }
250

251     public void setStyle(Object JavaDoc value) {
252         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot set style property");
253     }
254     
255     public void setCurrentStyle(Object JavaDoc value) {
256         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot set currentStyle property");
257     }
258
259     public String JavaDoc getClassName() {
260         String JavaDoc className = this.getAttribute("class");
261         // Blank required instead of null.
262
return className == null ? "" : className;
263     }
264     
265     public void setClassName(String JavaDoc className) {
266         this.setAttribute("class", className);
267     }
268     
269     public String JavaDoc getCharset() {
270         return this.getAttribute("charset");
271     }
272
273     public void setCharset(String JavaDoc charset) {
274         this.setAttribute("charset", charset);
275     }
276
277     public void warn(String JavaDoc message, Throwable JavaDoc err) {
278         logger.log(Level.WARNING, message, err);
279     }
280
281     public void warn(String JavaDoc message) {
282         logger.log(Level.WARNING, message);
283     }
284
285     protected int getAttributeAsInt(String JavaDoc name, int defaultValue) {
286         String JavaDoc value = this.getAttribute(name);
287         try {
288             return Integer.parseInt(value);
289         } catch(Exception JavaDoc err) {
290             this.warn("Bad integer", err);
291             return defaultValue;
292         }
293     }
294     
295     public boolean getAttributeAsBoolean(String JavaDoc name) {
296         String JavaDoc value = this.getAttribute(name);
297         return name.equalsIgnoreCase(value);
298     }
299     
300     protected void assignAttributeField(String JavaDoc normalName, String JavaDoc value) {
301         if(!this.notificationsSuspended) {
302             this.informInvalidAttibute(normalName);
303         }
304         else {
305             if("style".equals(normalName)) {
306                 this.forgetLocalStyle();
307             }
308         }
309         super.assignAttributeField(normalName, value);
310     }
311     
312     protected final InputSource getCssInputSourceForDecl(String JavaDoc text) {
313         java.io.Reader JavaDoc reader = new StringReader("{" + text + "}");
314         InputSource is = new InputSource(reader);
315         return is;
316     }
317     
318 /**
319  * Adds style sheet declarations applicable
320  * to this element.
321  * A properties object is created if necessary
322  * when the one passed is <code>null</code>.
323  * @param style
324  */

325     protected final CSS2PropertiesImpl addStyleSheetDeclarations(CSS2PropertiesImpl style) {
326         Node pn = this.parentNode;
327         if(pn == null) {
328             // do later
329
return style;
330         }
331         String JavaDoc classNames = this.getClassName();
332         if(classNames != null && classNames.length() != 0) {
333             String JavaDoc id = this.getId();
334             String JavaDoc elementName = this.getTagName();
335             String JavaDoc[] classNameArray = Strings.split(classNames);
336             for(int i = classNameArray.length; --i >= 0;) {
337                 String JavaDoc className = classNameArray[i];
338                 Collection JavaDoc sds = this.findStyleDeclarations(elementName, id, className);
339                 if(sds != null) {
340                     Iterator JavaDoc sdsi = sds.iterator();
341                     while(sdsi.hasNext()) {
342                         CSSStyleDeclaration sd = (CSSStyleDeclaration) sdsi.next();
343                         if(style == null) {
344                             style = new CSS2PropertiesImpl(this);
345                         }
346                         style.addStyleDeclaration(sd);
347                     }
348                 }
349             }
350         }
351         else {
352             String JavaDoc id = this.getId();
353             String JavaDoc elementName = this.getTagName();
354             Collection JavaDoc sds = this.findStyleDeclarations(elementName, id, null);
355             if(sds != null) {
356                 Iterator JavaDoc sdsi = sds.iterator();
357                 while(sdsi.hasNext()) {
358                     CSSStyleDeclaration sd = (CSSStyleDeclaration) sdsi.next();
359                     if(style == null) {
360                         style = new CSS2PropertiesImpl(this);
361                     }
362                     style.addStyleDeclaration(sd);
363                 }
364             }
365         }
366         return style;
367     }
368     
369     protected final Collection JavaDoc findStyleDeclarations(String JavaDoc elementName, String JavaDoc id, String JavaDoc className) {
370         HTMLDocumentImpl doc = (HTMLDocumentImpl) this.document;
371         if(doc == null) {
372             return null;
373         }
374         StyleSheetAggregator ssa = doc.getStyleSheetAggregator();
375         return ssa.getStyleDeclarations(this, elementName, id, className);
376     }
377     
378     public void informInvalid() {
379         // This is called when an attribute or child changes.
380
this.forgetStyle(false);
381         super.informInvalid();
382     }
383     
384     public void informInvalidAttibute(String JavaDoc normalName) {
385         // This is called when an attribute changes while
386
// the element is allowing notifications.
387
if("style".equals(normalName)) {
388             this.forgetLocalStyle();
389         }
390         else if("id".equals(normalName) || "class".equals(normalName)) {
391             this.forgetStyle(false);
392         }
393         // Call super implementation of informValid().
394
super.informInvalid();
395     }
396
397     public void informLayoutInvalid() {
398         // This is called by the style properties object
399
// when certain properties change.
400
super.informLayoutInvalid();
401     }
402
403     /**
404      * Gets form input due to the current element. It should
405      * return <code>null</code> except when the element is a form input element.
406      */

407     protected FormInput[] getFormInputs() {
408         // Override in input elements
409
return null;
410     }
411     
412     private boolean classMatch(String JavaDoc classTL) {
413         String JavaDoc classNames = this.getClassName();
414         if(classNames == null || classNames.length() == 0) {
415             return classTL == null;
416         }
417         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(classNames, " \t\r\n");
418         while(tok.hasMoreTokens()) {
419             String JavaDoc token = tok.nextToken();
420             if(token.toLowerCase().equals(classTL)) {
421                 return true;
422             }
423         }
424         return false;
425     }
426     
427     /**
428      * Get an ancestor that matches the element tag name given and the
429      * style class given.
430      * @param elementTL An tag name in lowercase or an asterisk (*).
431      * @param classTL A class name in lowercase.
432      */

433     public HTMLElementImpl getAncestorWithClass(String JavaDoc elementTL, String JavaDoc classTL) {
434         Object JavaDoc nodeObj = this.getParentNode();
435         if(nodeObj instanceof HTMLElementImpl) {
436             HTMLElementImpl parentElement = (HTMLElementImpl) nodeObj;
437             String JavaDoc pelementTL = parentElement.getTagName().toLowerCase();
438             if(("*".equals(elementTL) || elementTL.equals(pelementTL)) && parentElement.classMatch(classTL)) {
439                 return parentElement;
440             }
441             return parentElement.getAncestorWithClass(elementTL, classTL);
442         }
443         else {
444             return null;
445         }
446     }
447
448     public HTMLElementImpl getAncestorWithId(String JavaDoc elementTL, String JavaDoc idTL) {
449         Object JavaDoc nodeObj = this.getParentNode();
450         if(nodeObj instanceof HTMLElementImpl) {
451             HTMLElementImpl parentElement = (HTMLElementImpl) nodeObj;
452             String JavaDoc pelementTL = parentElement.getTagName().toLowerCase();
453             String JavaDoc pid = parentElement.getId();
454             String JavaDoc pidTL = pid == null ? null : pid.toLowerCase();
455             if(("*".equals(elementTL) || elementTL.equals(pelementTL)) && idTL.equals(pidTL)) {
456                 return parentElement;
457             }
458             return parentElement.getAncestorWithId(elementTL, idTL);
459         }
460         else {
461             return null;
462         }
463     }
464
465     public HTMLElementImpl getAncestor(String JavaDoc elementTL) {
466         Object JavaDoc nodeObj = this.getParentNode();
467         if(nodeObj instanceof HTMLElementImpl) {
468             HTMLElementImpl parentElement = (HTMLElementImpl) nodeObj;
469             if("*".equals(elementTL)) {
470                 return parentElement;
471             }
472             String JavaDoc pelementTL = parentElement.getTagName().toLowerCase();
473             if(elementTL.equals(pelementTL)) {
474                 return parentElement;
475             }
476             return parentElement.getAncestor(elementTL);
477         }
478         else {
479             return null;
480         }
481     }
482     
483     protected Object JavaDoc getAncestorForJavaClass(Class JavaDoc javaClass) {
484         Object JavaDoc nodeObj = this.getParentNode();
485         if(nodeObj == null || javaClass.isInstance(nodeObj)) {
486             return nodeObj;
487         }
488         else if(nodeObj instanceof HTMLElementImpl) {
489             return ((HTMLElementImpl) nodeObj).getAncestorForJavaClass(javaClass);
490         }
491         else {
492             return null;
493         }
494     }
495     
496     public void setInnerHTML(String JavaDoc newHtml) {
497         HTMLDocumentImpl document = (HTMLDocumentImpl) this.document;
498         if(document == null) {
499             this.warn("setInnerHTML(): Element " + this + " does not belong to a document.");
500             return;
501         }
502         HtmlParser parser = new HtmlParser(document.getUserAgentContext(), document, null, null, null);
503         synchronized(this) {
504             ArrayList JavaDoc nl = this.nodeList;
505             if (nl != null) {
506                 nl.clear();
507             }
508         }
509         // Should not synchronize around parser probably.
510
try {
511             Reader reader = new StringReader(newHtml);
512             try {
513                 parser.parse(reader, this);
514             } finally {
515                 reader.close();
516             }
517         } catch(Exception JavaDoc thrown) {
518             this.warn("setInnerHTML(): Error setting inner HTML.", thrown);
519         }
520     }
521
522     public String JavaDoc getInnerHTML() {
523         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
524         synchronized(this) {
525             this.appendInnerHTMLImpl(buffer);
526         }
527         return buffer.toString();
528     }
529
530     public String JavaDoc getOuterHTML() {
531         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
532         synchronized(this) {
533             this.appendOuterHTMLImpl(buffer);
534         }
535         return buffer.toString();
536     }
537
538     protected void appendInnerHTMLImpl(StringBuffer JavaDoc buffer) {
539         ArrayList JavaDoc nl = this.nodeList;
540         int size;
541         if (nl != null && (size = nl.size()) > 0) {
542             for (int i = 0; i < size; i++) {
543                 Node child = (Node) nl.get(i);
544                 if (child instanceof HTMLElementImpl) {
545                     ((HTMLElementImpl) child).appendOuterHTMLImpl(buffer);
546                 }
547                 else if(child instanceof Comment) {
548                     buffer.append("<!--" + ((Comment) child).getTextContent() + "-->");
549                 }
550                 else if (child instanceof Text) {
551                     String JavaDoc text = ((Text) child).getTextContent();
552                     String JavaDoc encText = Strings.strictHtmlEncode(text);
553                     buffer.append(encText);
554                 }
555             }
556         }
557     }
558
559     protected void appendOuterHTMLImpl(StringBuffer JavaDoc buffer) {
560         String JavaDoc tagName = this.getTagName();
561         buffer.append('<');
562         buffer.append(tagName);
563         Map JavaDoc attributes = this.attributes;
564         if(attributes != null) {
565             Iterator JavaDoc i = attributes.entrySet().iterator();
566             while(i.hasNext()) {
567                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
568                 String JavaDoc value = (String JavaDoc) entry.getValue();
569                 if(value != null) {
570                     buffer.append(' ');
571                     buffer.append(entry.getKey());
572                     buffer.append("=\"");
573                     buffer.append(Strings.strictHtmlEncode(value));
574                     buffer.append("\"");
575                 }
576             }
577         }
578         ArrayList JavaDoc nl = this.nodeList;
579         if(nl == null || nl.size() == 0) {
580             buffer.append("/>");
581             return;
582         }
583         buffer.append('>');
584         this.appendInnerHTMLImpl(buffer);
585         buffer.append("</");
586         buffer.append(tagName);
587         buffer.append('>');
588     }
589
590     protected RenderState createRenderState(RenderState prevRenderState) {
591         // Overrides NodeImpl method
592
// Called in synchronized block already
593
return new StyleSheetRenderState(prevRenderState, this);
594     }
595     
596     public int getOffsetTop() {
597         CSS2PropertiesImpl style = this.getCurrentStyle();
598         if(style == null) {
599             return 0;
600         }
601         String JavaDoc topText = style.getTop();
602         return topText == null ? 0 : HtmlValues.getPixelSize(topText, this.getRenderState(), 0);
603     }
604     
605     public int getOffsetLeft() {
606         CSS2PropertiesImpl style = this.getCurrentStyle();
607         if(style == null) {
608             return 0;
609         }
610         String JavaDoc leftText = style.getLeft();
611         return leftText == null ? 0 : HtmlValues.getPixelSize(leftText, this.getRenderState(), 0);
612     }
613
614     public int getOffsetWidth() {
615         CSS2PropertiesImpl style = this.getCurrentStyle();
616         if(style == null) {
617             return 0;
618         }
619         String JavaDoc valueText = style.getWidth();
620         return valueText == null ? 0 : HtmlValues.getPixelSize(valueText, this.getRenderState(), 0);
621     }
622
623     public int getOffsetHeight() {
624         CSS2PropertiesImpl style = this.getCurrentStyle();
625         if(style == null) {
626             return 0;
627         }
628         String JavaDoc valueText = style.getHeight();
629         return valueText == null ? 0 : HtmlValues.getPixelSize(valueText, this.getRenderState(), 0);
630     }
631
632     public CSS2PropertiesImpl getParentStyle() {
633         Object JavaDoc parent = this.parentNode;
634         if(parent instanceof HTMLElementImpl) {
635             return ((HTMLElementImpl) parent).getCurrentStyle();
636         }
637         return null;
638     }
639     
640     public String JavaDoc toString() {
641         return super.toString() + "[currentStyle=" + this.getCurrentStyle() + "]";
642     }
643 }
644
Popular Tags