0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.visualweb.designer.cssengine;
0043:
0044: import com.sun.org.apache.xerces.internal.dom.DocumentImpl;
0045: import org.netbeans.modules.visualweb.api.designer.cssengine.CssEngineService;
0046: import org.netbeans.modules.visualweb.api.designer.cssengine.CssProvider;
0047: import org.netbeans.modules.visualweb.api.designer.cssengine.CssValue;
0048: import org.netbeans.modules.visualweb.api.designer.cssengine.ResourceData;
0049: import org.netbeans.modules.visualweb.api.designer.cssengine.StyleData;
0050: import org.netbeans.modules.visualweb.api.designer.cssengine.XhtmlCss;
0051: import org.netbeans.modules.visualweb.spi.designer.cssengine.CssUserAgentInfo;
0052: import java.io.StringReader;
0053: import java.net.MalformedURLException;
0054: import java.net.URL;
0055: import java.util.ArrayList;
0056: import java.util.Collection;
0057: import java.util.Collections;
0058: import java.util.Iterator;
0059: import java.util.List;
0060: import java.util.Map;
0061: import java.util.WeakHashMap;
0062: import java.util.concurrent.locks.ReadWriteLock;
0063: import java.util.concurrent.locks.ReentrantReadWriteLock;
0064: import org.apache.batik.css.engine.CSSEngine;
0065: import org.apache.batik.css.engine.CSSStylableElement;
0066: import org.apache.batik.css.engine.CSSStyleSheetNode;
0067: import org.apache.batik.css.engine.ImportRule;
0068: import org.apache.batik.css.engine.Rule;
0069: import org.apache.batik.css.engine.StyleDeclaration;
0070: import org.apache.batik.css.engine.StyleMap;
0071: import org.apache.batik.css.engine.StyleRule;
0072: import org.apache.batik.css.engine.StyleSetting;
0073: import org.apache.batik.css.engine.StyleSheet;
0074: import org.apache.batik.css.engine.StyleSheetCache;
0075: import org.apache.batik.css.engine.value.AbstractValue;
0076: import org.apache.batik.css.engine.value.ComputedValue;
0077: import org.apache.batik.css.engine.value.IdentifierProvider;
0078: import org.apache.batik.css.engine.value.InheritValue;
0079: import org.apache.batik.css.engine.value.StringMap;
0080: import org.apache.batik.css.engine.value.URIValue;
0081: import org.apache.batik.css.engine.value.Value;
0082: import org.apache.batik.css.engine.value.ValueManager;
0083: import org.openide.ErrorManager;
0084: import org.openide.util.Lookup;
0085: import org.w3c.css.sac.CSSParseException;
0086: import org.w3c.css.sac.ErrorHandler;
0087: import org.w3c.css.sac.InputSource;
0088: import org.w3c.css.sac.SACMediaList;
0089: import org.w3c.dom.Document;
0090: import org.w3c.dom.DocumentFragment;
0091: import org.w3c.dom.Element;
0092: import org.w3c.dom.Node;
0093: import org.w3c.dom.UserDataHandler;
0094:
0095: /**
0096: * Impl of the <code>CssEngineService</code>.
0097: * XXX Impl code copied from various places before spread in the modules.
0098: *
0099: * @author Peter Zavadsky
0100: */
0101: public final class CssEngineServiceImpl implements CssEngineService {
0102:
0103: private static String[] properties;
0104:
0105: private static final CssEngineServiceImpl instance = new CssEngineServiceImpl();
0106:
0107: // /** Maps <code>Document</code> to XHTML CSS engine. */
0108: // private final Map<Document, XhtmlCssEngine> document2engine = new WeakHashMap<Document, XhtmlCssEngine>();
0109: private static final String KEY_CSS_ENGINE = "vwpXhtmlCssEngine"; // NOI18N
0110:
0111: /** Creates a new instance of CssServiceImpl */
0112: private CssEngineServiceImpl() {
0113: }
0114:
0115: public static CssEngineService getDefault() {
0116: return get();
0117: }
0118:
0119: static CssEngineServiceImpl get() {
0120: return instance;
0121: }
0122:
0123: // private static class EngineKey {}
0124: // private static final Map<EngineKey, XhtmlCssEngine> engineKey2engine = new WeakHashMap<EngineKey, XhtmlCssEngine>();
0125: // private static final ReadWriteLock engineLock = new ReentrantReadWriteLock();
0126: //
0127: // private static final Map<Document, EngineKey> doc2engineKey = new WeakHashMap<Document, EngineKey>();
0128:
0129: private XhtmlCssEngine getCssEngine(Document document) {
0130: if (document == null) {
0131: return null;
0132: }
0133: XhtmlCssEngine ret;
0134: // synchronized (document2engine) {
0135: // ret = document2engine.get(document);
0136: // }
0137: return (XhtmlCssEngine) document.getUserData(KEY_CSS_ENGINE);
0138: // engineLock.readLock().lock();
0139: // try {
0140: // EngineKey engineKey = doc2engineKey.get(document);
0141: // return engineKey2engine.get(engineKey);
0142: // } finally {
0143: // engineLock.readLock().unlock();
0144: // }
0145: }
0146:
0147: // public void setCssEngine(Document document, XhtmlCssEngine engine) {
0148: // synchronized (document2engine) {
0149: // document2engine.put(document, engine);
0150: // }
0151: // }
0152:
0153: public void createCssEngineForDocument(Document document, URL url) {
0154: if (document == null) {
0155: return;
0156: }
0157: CssUserAgentInfo userAgentInfo = getUserAgentInfo();
0158: XhtmlCssEngine engine = XhtmlCssEngine.create(document, url,
0159: userAgentInfo);
0160: // synchronized (document2engine) {
0161: // document2engine.put(document, engine);
0162: // }
0163: document.setUserData(KEY_CSS_ENGINE, engine,
0164: CssEngineDataHandler.getDefault());
0165: // engineLock.writeLock().lock();
0166: // try {
0167: // EngineKey engineKey = new EngineKey();
0168: // engineKey2engine.put(engineKey, engine);
0169: // doc2engineKey.put(document, engineKey);
0170: // } finally {
0171: // engineLock.writeLock().unlock();
0172: // }
0173: }
0174:
0175: /*private*/static CssUserAgentInfo getUserAgentInfo() {
0176: // XXX FIXME The userAgentInfo might not be correct as singleton, but it should be
0177: // passed as argument (there might be more user agents available).
0178: CssUserAgentInfo userAgentInfo = (CssUserAgentInfo) Lookup
0179: .getDefault().lookup(CssUserAgentInfo.class);
0180: if (userAgentInfo == null) {
0181: ErrorManager
0182: .getDefault()
0183: .notify(
0184: ErrorManager.INFORMATIONAL,
0185: new NullPointerException(
0186: "No CssUserAgentInfo available! Using dummy one!")); // NOI18N
0187: userAgentInfo = new DummyUserAgentInfo();
0188: }
0189: return userAgentInfo;
0190: }
0191:
0192: private static class DummyUserAgentInfo implements CssUserAgentInfo {
0193: public float getBlockWidth(Document document, Element element) {
0194: return 0F;
0195: }
0196:
0197: public float getBlockHeight(Document document, Element element) {
0198: return 0F;
0199: }
0200:
0201: public int getDefaultFontSize() {
0202: return 16;
0203: }
0204:
0205: public String computeFileName(Object location) {
0206: return location == null ? null : location.toString();
0207: }
0208:
0209: public int computeLineNumber(Object location, int lineno) {
0210: return lineno;
0211: }
0212:
0213: public URL getDocumentUrl(Document document) {
0214: return null;
0215: }
0216:
0217: public void displayErrorForLocation(String message,
0218: Object location, int lineno, int column) {
0219: }
0220:
0221: public Element getHtmlBodyForDocument(Document document) {
0222: return null;
0223: }
0224:
0225: public DocumentFragment getHtmlDomFragmentForDocument(
0226: Document document) {
0227: return null;
0228: }
0229: } // End of DummyBlockSizeProvider.
0230:
0231: public void removeCssEngineForDocument(Document document) {
0232: if (document == null) {
0233: return;
0234: }
0235:
0236: XhtmlCssEngine engine;
0237: // synchronized (document2engine) {
0238: // engine = document2engine.remove(document);
0239: // }
0240: engine = (XhtmlCssEngine) document.getUserData(KEY_CSS_ENGINE);
0241: // engineLock.readLock().lock();
0242: // try {
0243: // EngineKey engineKey = doc2engineKey.get(document);
0244: // engine = engineKey2engine.get(engineKey);
0245: // } finally {
0246: // engineLock.readLock().unlock();
0247: // }
0248:
0249: if (engine != null) {
0250: engine.dispose();
0251: }
0252: }
0253:
0254: public void reuseCssEngineForDocument(Document document,
0255: Document originalDocument) {
0256: if (document == null || originalDocument == null) {
0257: return;
0258: }
0259: // synchronized (document2engine) {
0260: // XhtmlCssEngine engine = document2engine.get(originalDocument);
0261: // document2engine.put(document, engine);
0262: // }
0263: XhtmlCssEngine engine = (XhtmlCssEngine) originalDocument
0264: .getUserData(KEY_CSS_ENGINE);
0265: document.setUserData(KEY_CSS_ENGINE, engine,
0266: CssEngineDataHandler.getDefault());
0267: // engineLock.writeLock().lock();
0268: // try {
0269: // EngineKey originalEngineKey = doc2engineKey.get(originalDocument);
0270: // XhtmlCssEngine engine = engineKey2engine.get(originalEngineKey);
0271: // EngineKey engineKey = new EngineKey();
0272: // engineKey2engine.put(engineKey, engine);
0273: // doc2engineKey.put(document, engineKey);
0274: // } finally {
0275: // engineLock.writeLock().unlock();
0276: // }
0277: }
0278:
0279: public Collection<String> getCssStyleClassesForDocument(
0280: Document document) {
0281: XhtmlCssEngine engine = getCssEngine(document);
0282:
0283: if (engine == null) {
0284: return Collections.emptySet();
0285: } else {
0286: return engine.getStyleClasses();
0287: }
0288: }
0289:
0290: public Map<String, String> getStyleMapFromStringForDocument(
0291: Document document, String style) {
0292: XhtmlCssEngine engine = getCssEngine(document);
0293:
0294: if (engine == null) {
0295: return Collections.emptyMap();
0296: } else {
0297: return engine.styleToMap(style);
0298: }
0299: }
0300:
0301: public String getStringFromStyleMapForDocument(Document document,
0302: Map<String, String> styleMap) {
0303: XhtmlCssEngine engine = getCssEngine(document);
0304:
0305: if (engine == null) {
0306: return ""; // NOI18N
0307: } else {
0308: return engine.mapToStyle(styleMap);
0309: }
0310: }
0311:
0312: public void addTransientStyleSheetNodeForDocument(
0313: Document document, Node styleSheetNode) {
0314: if (!(styleSheetNode instanceof CSSStyleSheetNode)) {
0315: ErrorManager.getDefault().notify(
0316: ErrorManager.INFORMATIONAL,
0317: new IllegalArgumentException(
0318: "Node has to be of CSSStyleSheetNode type, node="
0319: + styleSheetNode)); // NOI18N
0320: return;
0321: }
0322:
0323: XhtmlCssEngine engine = getCssEngine(document);
0324:
0325: if (engine != null) {
0326: engine
0327: .addTransientStyleSheetNode((CSSStyleSheetNode) styleSheetNode);
0328: }
0329: }
0330:
0331: public void clearTransientStyleSheetNodesForDocument(
0332: Document document) {
0333: XhtmlCssEngine engine = getCssEngine(document);
0334:
0335: if (engine != null) {
0336: engine.clearTransientStyleSheetNodes();
0337: }
0338: }
0339:
0340: // public void addLocalStyleValueForElement(Element element, int style, String value) {
0341: // List set = new ArrayList(1);
0342: // set.add(new StyleData(style, value));
0343: // updateLocalStyleValuesForElement(element,(StyleData[])set.toArray(new StyleData[set.size()]), null);
0344: // }
0345: //
0346: // public void removeLocalStyleValueForElement(Element element, int style) {
0347: // List remove = new ArrayList(1);
0348: // remove.add(new StyleData(style));
0349: // updateLocalStyleValuesForElement(element, null, (StyleData[])remove.toArray(new StyleData[remove.size()]));
0350: // }
0351: //
0352: // public void updateLocalStyleValuesForElement(Element element, StyleData[] stylesToSet, StyleData[] stylesToRemove) {
0353: // try {
0354: //// String value = engine.getUpdatedLocalStyleValues(element, set, remove);
0355: // String value = getUpdatedLocalStyleValuesForElement(element, stylesToSet, stylesToRemove);
0356: //
0357: //// if (element instanceof RaveElement) {
0358: //// RaveElement raveElement = (RaveElement)element;
0359: ////// DesignBean markupBean = raveElement.getDesignBean();
0360: //// DesignBean markupBean = InSyncService.getProvider().getMarkupDesignBeanForElement(raveElement);
0361: // if (element != null) {
0362: // DesignBean markupBean = InSyncService.getProvider().getMarkupDesignBeanForElement(element);
0363: // if (markupBean != null) {
0364: // DesignProperty property = markupBean.getProperty(HtmlAttribute.STYLE);
0365: // if (property != null) {
0366: // try {
0367: // if ((value != null) && (value.length() > 0)) {
0368: // property.setValue(value);
0369: // } else {
0370: // property.unset();
0371: // }
0372: //
0373: // return;
0374: // } catch (Exception ex) {
0375: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
0376: // // For some reason the above throws exceptions
0377: // // sometimes, not sure why org.w3c.dom.DOMException:
0378: // // NOT_FOUND_ERR: An attempt is made to reference a
0379: // // node in a context where it does not exist. TODO
0380: // // - figure out WHY! For now just swallow since
0381: // // there's nothing more we can do about it.
0382: // // (Update: It think this may be fixed now)
0383: // }
0384: // }
0385: // }
0386: // }
0387: //
0388: // // If the above fails (shouldn't)
0389: //// engine.setStyleAttributeValue(element, value);
0390: // setStyleAttributeForElement(element, value);
0391: // } catch (Exception e) {
0392: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
0393: // }
0394: // }
0395:
0396: public String getUpdatedLocalStyleValuesForElement(Element element,
0397: StyleData[] stylesToSet, StyleData[] stylesToRemove)
0398: throws Exception {
0399: if (!(element instanceof CSSStylableElement)) {
0400: return ""; // NOI18N
0401: }
0402:
0403: Document document = element.getOwnerDocument();
0404: XhtmlCssEngine engine = getCssEngine(document);
0405:
0406: if (engine != null) {
0407: CSSStylableElement elt = (CSSStylableElement) element;
0408: return engine.getUpdatedLocalStyleValues(elt,
0409: convertToStyleSettings(stylesToSet),
0410: convertToStyleSettings(stylesToRemove));
0411: } else {
0412: return ""; // NOI18N
0413: }
0414: }
0415:
0416: private static StyleSetting[] convertToStyleSettings(
0417: StyleData[] stylesData) {
0418: if (stylesData == null) {
0419: return null;
0420: }
0421:
0422: List<StyleSetting> styleSettings = new ArrayList<StyleSetting>();
0423: for (int i = 0; i < stylesData.length; i++) {
0424: StyleData sd = stylesData[i];
0425: styleSettings.add(new StyleSetting(sd.getIndex(), sd
0426: .getValue()));
0427: }
0428: return styleSettings.toArray(new StyleSetting[styleSettings
0429: .size()]);
0430: }
0431:
0432: public void setStyleAttributeForElement(Element element,
0433: String value) {
0434: if (element == null) {
0435: return;
0436: }
0437:
0438: Document document = element.getOwnerDocument();
0439: XhtmlCssEngine engine = getCssEngine(document);
0440:
0441: if (engine != null) {
0442: engine.setStyleAttributeValue(element, value);
0443: }
0444: }
0445:
0446: public void clearComputedStylesForElement(Element element) {
0447: if (element == null) {
0448: return;
0449: }
0450:
0451: Document document = element.getOwnerDocument();
0452: XhtmlCssEngine engine = getCssEngine(document);
0453:
0454: if (engine != null) {
0455: engine.clearComputedStyles(element);
0456: }
0457: }
0458:
0459: public void setSilentErrorHandlerForDocument(Document document) {
0460: setErrorHandlerForDocument(document,
0461: XhtmlCssEngine.SILENT_ERROR_HANDLER);
0462: }
0463:
0464: public void setNullErrorHandlerForDocument(Document document) {
0465: setErrorHandlerForDocument(document, null);
0466: }
0467:
0468: private void setErrorHandlerForDocument(Document document,
0469: ErrorHandler errorHandler) {
0470: XhtmlCssEngine engine = getCssEngine(document);
0471:
0472: if (engine != null) {
0473: engine.setErrorHandler(errorHandler);
0474: }
0475: }
0476:
0477: public boolean isInheritedStyleValueForElement(Element element,
0478: int propIndex) {
0479: if (!(element instanceof CSSStylableElement)) {
0480: return false;
0481: }
0482:
0483: Document document = element.getOwnerDocument();
0484: XhtmlCssEngine engine = getCssEngine(document);
0485:
0486: if (engine != null) {
0487: CSSStylableElement elt = (CSSStylableElement) element;
0488: return engine.isInheritedValue(elt, propIndex);
0489: } else {
0490: return false;
0491: }
0492: }
0493:
0494: public boolean isDefaultStyleValueForElement(Element element,
0495: String pseudo, int propIndex) {
0496: if (!(element instanceof CSSStylableElement)) {
0497: return false;
0498: }
0499:
0500: Document document = element.getOwnerDocument();
0501: XhtmlCssEngine engine = getCssEngine(document);
0502:
0503: if (engine != null) {
0504: CSSStylableElement elt = (CSSStylableElement) element;
0505: return engine.isDefaultValue(elt, pseudo, propIndex);
0506: } else {
0507: return false;
0508: }
0509: }
0510:
0511: public void refreshStylesForDocument(Document document) {
0512: XhtmlCssEngine engine = getCssEngine(document);
0513:
0514: if (engine != null) {
0515: engine.refreshStyles();
0516: }
0517: }
0518:
0519: // <Non engine methods, might be separated>
0520: // XXX Moved from XhtmlCssEngine.
0521: public boolean isInlineStyleValue(Element element, int propidx) {
0522: if (!(element instanceof CSSStylableElement)) {
0523: return false;
0524: }
0525:
0526: CSSStylableElement elt = (CSSStylableElement) element;
0527:
0528: String pseudo = ""; // Pending // NOI18N
0529: StyleMap sm = elt.getComputedStyleMap(pseudo);
0530:
0531: if (sm == null) {
0532: return false;
0533: }
0534:
0535: Value value = sm.getValue(propidx);
0536:
0537: if (value == null) {
0538: return false;
0539: }
0540:
0541: return sm.getOrigin(propidx) == StyleMap.INLINE_AUTHOR_ORIGIN;
0542: }
0543:
0544: // </Non engine methods, might be separated>
0545:
0546: // <dominspector?>
0547: public String getAllStylesForElement(Element element) {
0548: if (!(element instanceof CSSStylableElement)) {
0549: return "";
0550: }
0551:
0552: Document document = element.getOwnerDocument();
0553: XhtmlCssEngine engine = getCssEngine(document);
0554:
0555: if (engine != null) {
0556: CSSStylableElement elt = (CSSStylableElement) element;
0557: StyleMap map = elt.getComputedStyleMap(null);
0558: if (map != null) {
0559: return map.toString(engine);
0560: }
0561: }
0562:
0563: return ""; // NOI18N
0564: }
0565:
0566: // XXX Content moved from designer/CssLookup.
0567: public String getAllComputedStylesForElement(Element element) {
0568: if (!(element instanceof CSSStylableElement)) {
0569: return "";
0570: }
0571:
0572: Document document = element.getOwnerDocument();
0573: XhtmlCssEngine engine = getCssEngine(document);
0574:
0575: if (engine == null) {
0576: return ""; // NOI18N
0577: }
0578:
0579: CSSStylableElement elt = (CSSStylableElement) element;
0580: StyleMap map = elt.getComputedStyleMap(null);
0581: // StyleMap cascaded = getCascadedStyleMap(elt);
0582: StyleMap cascaded = engine.getCascadedStyleMap(elt, null);
0583:
0584: StringBuffer sb = new StringBuffer(400);
0585:
0586: for (int i = 0, n = map.getSize(true); i < n; i++) {
0587: Value v = engine.getComputedStyle(
0588: (CSSStylableElement) element, null, i);
0589:
0590: ValueManager vm = engine.getValueManagers()[i];
0591: Value deflt = vm.getDefaultValue();
0592:
0593: if (!v.equals(deflt)) { // XXX Use !.equals instead?
0594: sb.append("*");
0595: }
0596:
0597: int pos = sb.length();
0598: sb.append(engine.getPropertyName(i));
0599: sb.append(": ");
0600: sb.append(v);
0601:
0602: sb.append(" ");
0603:
0604: if (cascaded.getOrigin(i) == StyleMap.NON_CSS_ORIGIN) {
0605: sb.append("(from HTML attribute) ");
0606: }
0607:
0608: if (cascaded.getValue(i) == InheritValue.INSTANCE) {
0609: sb.append("(inherited) ");
0610: }
0611:
0612: if (map.isImportant(i)) {
0613: sb.append("!important ");
0614: }
0615:
0616: if (v instanceof AbstractValue) {
0617: AbstractValue av = (AbstractValue) v;
0618: // String fullname = ((XhtmlCssEngine)engine).computeFilename(av);
0619: // String fullname = InSyncService.getProvider().computeFileName(av.getLocation());
0620: String fullname = engine.computeFileName(av
0621: .getLocation());
0622:
0623: if (fullname != null) {
0624: String location = null;
0625: String filename = fullname.substring(fullname
0626: .lastIndexOf('/') + 1);
0627:
0628: // +1: numbers are generally 1-based
0629: // int lineno = ((XhtmlCssEngine)engine).computeLineNumber(av) + 1;
0630: // int lineno = InSyncService.getProvider().computeLineNumber(av.getLocation(), av.getLineNumber()) + 1;
0631: int lineno = engine.computeLineNumber(av
0632: .getLocation(), av.getLineNumber()) + 1;
0633:
0634: if ((filename.length() == 0)
0635: || filename.equals("default.css")) { // NOI18N
0636: } else if ((lineno > 0) && (filename.length() > 0)) {
0637: location = filename + ':'
0638: + Integer.toString(lineno);
0639: } else {
0640: location = filename;
0641: }
0642:
0643: if (location != null) {
0644: int currPos = sb.length();
0645: sb.append(' ');
0646:
0647: while (currPos < (pos + 40)) {
0648: sb.append(' ');
0649: currPos++;
0650: }
0651:
0652: sb.append(location);
0653: }
0654: }
0655: }
0656:
0657: sb.append('\n');
0658: }
0659:
0660: return sb.toString();
0661: }
0662:
0663: public String getAllRulesForElement(Element element) {
0664: if (element == null) {
0665: return ""; // NOI18N
0666: }
0667:
0668: Document document = element.getOwnerDocument();
0669: XhtmlCssEngine engine = getCssEngine(document);
0670:
0671: if (engine != null) {
0672: return engine.getMatchingRules(element, false);
0673: } else {
0674: return ""; // NOI18N
0675: }
0676: }
0677:
0678: // </dominspector?>
0679:
0680: // XXX
0681: public void uncomputeValueForElement(Element element, int propIndex) {
0682: if (!(element instanceof CSSStylableElement)) {
0683: return;
0684: }
0685:
0686: Document document = element.getOwnerDocument();
0687: XhtmlCssEngine engine = getCssEngine(document);
0688:
0689: if (engine == null) {
0690: return;
0691: }
0692:
0693: // RaveElement xel = (RaveElement)e;
0694: CSSStylableElement elt = (CSSStylableElement) element;
0695: StyleMap sm = elt.getComputedStyleMap(null);
0696:
0697: if (sm != null) {
0698: Value v = sm.getValue(propIndex);
0699:
0700: if (v instanceof ComputedValue) {
0701: ComputedValue cv = (ComputedValue) v;
0702: sm.putComputed(propIndex, false);
0703: sm.putValue(propIndex, cv.getCascadedValue());
0704: }
0705: }
0706: }
0707:
0708: public CssValue getComputedValueForElement(Element element,
0709: int propIndex) {
0710: return getComputedValueImplForElement(element, propIndex);
0711: }
0712:
0713: CssValueImpl getComputedValueImplForElement(Element element,
0714: int propIndex) {
0715: if (!(element instanceof CSSStylableElement)) {
0716: return null;
0717: }
0718:
0719: Document document = element.getOwnerDocument();
0720: XhtmlCssEngine engine = getCssEngine(document);
0721:
0722: if (engine == null) {
0723: ErrorManager.getDefault().notify(
0724: ErrorManager.INFORMATIONAL,
0725: new IllegalStateException(
0726: "There is no css engine associated with the element's document," // NOI18N
0727: + "\nelement="
0728: + element // NOI18N
0729: + "\ndocument="
0730: + document // NOI18N
0731: + "\ndocument class="
0732: + (document == null ? null
0733: : document.getClass()) // NOI18N
0734: + "\ndocument hashCode="
0735: + (document == null ? null
0736: : document.hashCode()))); // NOI18N
0737: return null;
0738: }
0739:
0740: CSSStylableElement elt = (CSSStylableElement) element;
0741: Value value = engine.getComputedStyle(elt, null, propIndex);
0742:
0743: return (CssValueImpl) CssValueFactory.createCssValue(value);
0744: }
0745:
0746: private boolean isMediaMatchingForDocument(Document document,
0747: SACMediaList mediaList) {
0748: XhtmlCssEngine engine = getCssEngine(document);
0749:
0750: if (engine == null) {
0751: return false;
0752: }
0753:
0754: return engine.mediaMatch(mediaList);
0755: }
0756:
0757: private StyleDeclaration parseStyleDeclarationForElement(
0758: Element element, String value) {
0759: if (!(element instanceof CSSStylableElement)) {
0760: return new StyleDeclaration();
0761: }
0762:
0763: Document document = element.getOwnerDocument();
0764: XhtmlCssEngine engine = getCssEngine(document);
0765:
0766: if (engine == null) {
0767: return new StyleDeclaration();
0768: }
0769:
0770: CSSStylableElement elt = (CSSStylableElement) element;
0771: return engine.parseStyleDeclaration(elt, value);
0772: }
0773:
0774: private StyleSheet parseStyleSheetForDocument(Document document,
0775: InputSource inputSource, URL uri, String media,
0776: Object location) {
0777: XhtmlCssEngine engine = getCssEngine(document);
0778:
0779: if (engine == null) {
0780: return new StyleSheet();
0781: }
0782:
0783: return engine
0784: .parseStyleSheet(inputSource, uri, media, location);
0785: }
0786:
0787: ////////////////////////////////////////////////////////////////////////////
0788: // <methods needed to be replaced>
0789: // FIXME get rid of this method (batik return type) api has to be batik independent.
0790: public StyleSheet parseStyleSheetForDocument(Document document,
0791: String rules, URL uri, String media, Object location) {
0792: XhtmlCssEngine engine = getCssEngine(document);
0793:
0794: if (engine == null) {
0795: return new StyleSheet();
0796: }
0797:
0798: return engine.parseStyleSheet(rules, uri, media, location);
0799: }
0800:
0801: public StyleSheet parseStyleSheetForDocument(Document document,
0802: URL uri, String media, Object location) {
0803: XhtmlCssEngine engine = getCssEngine(document);
0804:
0805: if (engine == null) {
0806: return new StyleSheet();
0807: }
0808:
0809: return engine.parseStyleSheet(uri, media, location);
0810: }
0811:
0812: // </methods needed to be replaced>
0813: ////////////////////////////////////////////////////////////////////////////
0814:
0815: private static ValueManager[] getXhtmlValueManagers() {
0816: return XhtmlCssEngine.XHTML_VALUE_MANAGERS;
0817: }
0818:
0819: public int getXhtmlPropertyIndex(String property) {
0820: return XhtmlCssEngine.getXhtmlPropertyIndex(property);
0821: }
0822:
0823: private static int getXhtmlShorthandIndex(String property) {
0824: return XhtmlCssEngine.getXhtmlShorthandIndex(property);
0825: }
0826:
0827: public Element createPreviewElementForDocument(Document document,
0828: URL base, String styles) {
0829: return new PreviewElement(document, base, styles);
0830: }
0831:
0832: public void clearEngineStyleLookupCount() {
0833: CSSEngine.styleLookupCount = 0;
0834: }
0835:
0836: public int getEngineStyleLookupCount() {
0837: return CSSEngine.styleLookupCount;
0838: }
0839:
0840: public void flushStyleSheetCache() {
0841: // XXX
0842: StyleSheetCache.getInstance().flush();
0843: }
0844:
0845: public String[] getCssIdentifiers(String propertyName) {
0846: StringMap map = getIdentifiers(propertyName);
0847:
0848: if (map == null) {
0849: return new String[0];
0850: }
0851:
0852: int count = map.size();
0853: List<String> keys = new ArrayList<String>(count);
0854: Iterator<String> it = map.keys();
0855:
0856: while (it.hasNext()) {
0857: String string = it.next();
0858: keys.add(string);
0859: }
0860:
0861: keys.add("inherit"); // NOI18N
0862: Collections.sort(keys);
0863:
0864: return keys.toArray(new String[keys.size()]);
0865: }
0866:
0867: private StringMap getIdentifiers(String property) {
0868: // int index = XhtmlCssEngine.getXhtmlPropertyIndex(property);
0869: int index = getXhtmlPropertyIndex(property);
0870:
0871: if (index == -1) {
0872: // index = XhtmlCssEngine.getXhtmlShorthandIndex(property);
0873: index = getXhtmlShorthandIndex(property);
0874:
0875: if (index == -1) {
0876: return null;
0877: }
0878:
0879: // XXX TODO! What do we do here?
0880: return null;
0881: }
0882:
0883: // ValueManager vm = XhtmlCssEngine.XHTML_VALUE_MANAGERS[index];
0884: ValueManager vm = getXhtmlValueManagers()[index];
0885:
0886: if (vm instanceof IdentifierProvider) {
0887: return ((IdentifierProvider) vm).getIdentifierMap();
0888: }
0889:
0890: return null;
0891: }
0892:
0893: public String[] getCssProperties() {
0894: if (properties == null) {
0895: // ValueManager[] vms = XhtmlCssEngine.XHTML_VALUE_MANAGERS;
0896: ValueManager[] vms = getXhtmlValueManagers();
0897: List<String> list = new ArrayList<String>(vms.length);
0898:
0899: for (int i = 0, n = vms.length; i < n; i++) {
0900: String property = vms[i].getPropertyName();
0901:
0902: if (property.charAt(0) != '-') { // don't include vendor-specific properties
0903: list.add(property);
0904: }
0905: }
0906:
0907: Collections.sort(list);
0908: properties = list.toArray(new String[list.size()]);
0909: }
0910:
0911: return properties;
0912: }
0913:
0914: public void setStyleParentForElement(Element element,
0915: Element styleParent) {
0916: if (element == styleParent) {
0917: // #6490385 Don't do that for the same instances.
0918: return;
0919: }
0920: if (element instanceof CSSStylableElement
0921: && styleParent instanceof CSSStylableElement) {
0922: CSSEngine.StyleElementLink link = (CSSEngine.StyleElementLink) element;
0923: link.setStyleParent((CSSStylableElement) styleParent);
0924: }
0925: }
0926:
0927: public Element getStyleParentForElement(Element element) {
0928: if (element instanceof CSSEngine.StyleElementLink) {
0929: return ((CSSEngine.StyleElementLink) element)
0930: .getStyleParent();
0931: }
0932: return null;
0933: }
0934:
0935: public String[] getStyleResourcesForElement(Element element,
0936: String rules, Document doc, URL docUrl, int[] indexesToMatch) {
0937: // URL docUrl = InSyncService.getProvider().getUrl(doc);
0938: createCssEngineForDocument(doc, docUrl);
0939: StyleDeclaration sd = parseStyleDeclarationForElement(element,
0940: rules);
0941: return getStyleResourcesFromStyleDeclaration(sd, indexesToMatch);
0942: }
0943:
0944: public ResourceData[] getStyleResourcesForRules(String rules,
0945: Document doc, URL docUrl, URL base, int[] indexesToMatch) {
0946: // URL docUrl = InSyncService.getProvider().getUrl(doc);
0947: createCssEngineForDocument(doc, docUrl);
0948: InputSource is = new InputSource(new StringReader(rules));
0949: StyleSheet ss = parseStyleSheetForDocument(doc, is, base,
0950: "all", base); // NOI18N
0951: return getStyleResourcesFromStyleSheet(doc, ss, indexesToMatch);
0952: }
0953:
0954: public ResourceData[] getStyleResourcesForUrl(URL url,
0955: Document doc, URL docUrl, int[] indexesToMatch) {
0956: // URL docUrl = InSyncService.getProvider().getUrl(doc);
0957: createCssEngineForDocument(doc, docUrl);
0958: InputSource is = new InputSource(url.toString());
0959: StyleSheet ss = parseStyleSheetForDocument(doc, is, url, "all",
0960: url); // NOI18N
0961: return getStyleResourcesFromStyleSheet(doc, ss, indexesToMatch);
0962: }
0963:
0964: /**
0965: * Gets an array of urlStrings.
0966: * Adds the rules matching the element/pseudo-element of given style
0967: * declaration to the list
0968: */
0969: private static String[] getStyleResourcesFromStyleDeclaration(
0970: StyleDeclaration sd, int[] indexesToMatch) {
0971: List<String> urlStrings = new ArrayList<String>();
0972: for (int i = 0, m = sd.size(); i < m; i++) {
0973: int idx = sd.getIndex(i);
0974:
0975: boolean matches = false;
0976: for (int j = 0; j < indexesToMatch.length; j++) {
0977: if (idx == indexesToMatch[j]) {
0978: matches = true;
0979: break;
0980: }
0981: }
0982:
0983: // if ((idx == XhtmlCss.BACKGROUND_IMAGE_INDEX) ||
0984: // (idx == XhtmlCss.LIST_STYLE_IMAGE_INDEX)) {
0985: if (matches) {
0986: // If I support audio: cue-before, cure-after,
0987: // play-during as well
0988: Value v = sd.getValue(i);
0989:
0990: if (v instanceof URIValue) {
0991: URIValue uv = (URIValue) v;
0992: String urlString = uv.getRawCssText();
0993:
0994: // if (rewrite.get(urlString) == null) {
0995: // // Import the image, as newUrl
0996: // String projectUrl = copyResource(urlString);
0997: //
0998: // if (projectUrl != null) {
0999: // rewrite.put(urlString, projectUrl);
1000: // }
1001: // }
1002: if (urlStrings.contains(urlString)) {
1003: continue;
1004: }
1005:
1006: urlStrings.add(urlString);
1007: }
1008: }
1009: }
1010: return urlStrings.toArray(new String[urlStrings.size()]);
1011: }
1012:
1013: /** Gets map of urlString to relPath.
1014: * Adds the rules matching the element/pseudo-element of given style
1015: * sheet to the list.
1016: */
1017: private ResourceData[] getStyleResourcesFromStyleSheet(
1018: Document doc, StyleSheet ss, int[] indexesToMatch) {
1019: // Map rewrite = new HashMap();
1020: List<ResourceData> resourceData = new ArrayList<ResourceData>();
1021:
1022: int len = ss.getSize();
1023:
1024: for (int i = 0; i < len; i++) {
1025: Rule r = ss.getRule(i);
1026:
1027: switch (r.getType()) {
1028: case StyleRule.TYPE:
1029:
1030: StyleRule style = (StyleRule) r;
1031: StyleDeclaration sd = style.getStyleDeclaration();
1032: // rewrite.putAll(importStyleResourcesFromStyleDeclaration(depth, sd));
1033: String[] urlStrings = getStyleResourcesFromStyleDeclaration(
1034: sd, indexesToMatch);
1035: // rewrite.putAll(importStyleResources(urlStrings));
1036: resourceData
1037: .add(new UrlStringsResourceData(urlStrings));
1038: break;
1039:
1040: /*case MediaRule.TYPE:*/
1041: case ImportRule.TYPE:
1042:
1043: ImportRule mr = (ImportRule) r;
1044:
1045: // XhtmlCssEngine ces = CssEngineServiceProvider.getDefault().getCssEngine(doc);
1046: // if (ces.mediaMatch(mr.getMediaList())) { // XXX todo
1047: if (isMediaMatchingForDocument(doc, mr.getMediaList())) {
1048:
1049: URL url = mr.getURI();
1050: // String parent = new File(url.getPath()).getParent() + "/";
1051: // URL oldBase = context.base;
1052: //
1053: // try {
1054: // context.base =
1055: // new URL(url.getProtocol(), url.getHost(), url.getPort(), parent);
1056: // } catch (MalformedURLException mfu) {
1057: // // XXX shouldn't happen
1058: // ErrorManager.getDefault().notify(mfu);
1059: //
1060: // return rewrite;
1061: // }
1062: //
1063: // String urlString = mr.getRelativeUri();
1064: // String relPath = handleStyleSheet(depth + 1, urlString, url);
1065: // context.base = oldBase;
1066: String urlString = mr.getRelativeUri();
1067: // String relPath;
1068: // try {
1069: // relPath = importStyleSheetResource(depth, url, urlString);
1070: // } catch (MalformedURLException mfu) {
1071: // // XXX shouldn't happen
1072: // ErrorManager.getDefault().notify(mfu);
1073: // return rewrite;
1074: // }
1075: //
1076: // if (relPath != null) {
1077: // rewrite.put(urlString, relPath);
1078: // }
1079: resourceData
1080: .add(new UrlResourceData(url, urlString));
1081: }
1082:
1083: break;
1084: }
1085: }
1086:
1087: return resourceData.toArray(new ResourceData[resourceData
1088: .size()]);
1089: }
1090:
1091: public CssSyntaxErrorInfo[] parseCss(
1092: javax.swing.text.Document document) {
1093: if (document == null) {
1094: throw new IllegalArgumentException(
1095: "Parameter document may not be null!"); // NOI18N
1096: }
1097:
1098: Document fakeDocument = new FakeDocument();
1099: createCssEngineForDocument(fakeDocument, null);
1100:
1101: List<CssSyntaxErrorInfo> parseErrors = new ArrayList<CssSyntaxErrorInfo>();
1102: ErrorHandler handler = new DefaultErrorHandler(parseErrors);
1103: setErrorHandlerForDocument(fakeDocument, handler);
1104:
1105: String rules;
1106:
1107: try {
1108: rules = document.getText(0, document.getLength());
1109: } catch (javax.swing.text.BadLocationException e) {
1110: ErrorManager.getDefault().notify(e);
1111:
1112: return parseErrors
1113: .toArray(new CssSyntaxErrorInfo[parseErrors.size()]);
1114: }
1115:
1116: // engine.parseStyleSheet(rules, null, "all", null);
1117: parseStyleSheetForDocument(fakeDocument, rules, null, "all",
1118: null); // NOI18N
1119: // engine.setErrorHandler(null);
1120: setErrorHandlerForDocument(fakeDocument, null);
1121:
1122: return parseErrors.toArray(new CssSyntaxErrorInfo[parseErrors
1123: .size()]);
1124: }
1125:
1126: public URL getBackgroundImageUrlForElement(Element element,
1127: URL baseUrl) {
1128: // Value value = CssLookup.getValue(element, XhtmlCss.BACKGROUND_IMAGE_INDEX);
1129: CssValue cssValue = getComputedValueForElement(element,
1130: XhtmlCss.BACKGROUND_IMAGE_INDEX);
1131:
1132: // if (value == CssValueConstants.NONE_VALUE) {
1133: if (CssProvider.getValueService().isNoneValue(cssValue)) {
1134: return null;
1135: }
1136:
1137: // String urlString = value.getStringValue();
1138: String urlString = cssValue.getStringValue();
1139: try {
1140: return new URL(baseUrl, urlString);
1141: } catch (MalformedURLException ex) {
1142: ErrorManager.getDefault().notify(
1143: ErrorManager.INFORMATIONAL, ex);
1144: }
1145:
1146: return null;
1147: }
1148:
1149: private static class UrlStringsResourceData implements
1150: ResourceData.UrlStringsResourceData {
1151: private final String[] urlStrings;
1152:
1153: public UrlStringsResourceData(String[] urlStrings) {
1154: this .urlStrings = urlStrings;
1155: }
1156:
1157: public String[] getUrlStrings() {
1158: return urlStrings;
1159: }
1160: } // End of UrlStringsResourceData.
1161:
1162: private static class UrlResourceData implements
1163: ResourceData.UrlResourceData {
1164: private final URL url;
1165: private final String urlString;
1166:
1167: public UrlResourceData(URL url, String urlString) {
1168: this .url = url;
1169: this .urlString = urlString;
1170: }
1171:
1172: public URL getUrl() {
1173: return url;
1174: }
1175:
1176: public String getUrlString() {
1177: return urlString;
1178: }
1179: } // End of UrlResourceData.
1180:
1181: /** XXX Fake document, to be able to create engine.
1182: * TODO Better is just to impl the <code>Document</code> interface, without dep on xerces. */
1183: private static class FakeDocument extends DocumentImpl {
1184: } // End of FakeDocument.
1185:
1186: // XXX Add the error type and error code also.
1187: private static class DefaultErrorHandler implements ErrorHandler {
1188: private final List<CssSyntaxErrorInfo> parseErrors;
1189:
1190: public DefaultErrorHandler(List<CssSyntaxErrorInfo> parseErrors) {
1191: this .parseErrors = parseErrors;
1192: }
1193:
1194: public void warning(CSSParseException cpe) {
1195: //System.out.println("CSS Parse Warning");
1196: //System.out.println(cpe.getLineNumber() + ": " + cpe.getLocalizedMessage());
1197: parseErrors.add(new DefaultCssSyntaxErrorInfo(cpe
1198: .getLineNumber(), cpe.getLocalizedMessage()));
1199: }
1200:
1201: public void error(CSSParseException cpe) {
1202: //System.out.println("CSS Parse Error");
1203: //System.out.println(cpe.getLineNumber() + ": " + cpe.getLocalizedMessage());
1204: parseErrors.add(new DefaultCssSyntaxErrorInfo(cpe
1205: .getLineNumber(), cpe.getLocalizedMessage()));
1206: }
1207:
1208: public void fatalError(CSSParseException cpe) {
1209: //System.out.println("CSS Parse Fatal Error");
1210: //System.out.println(cpe.getLineNumber() + ": " + cpe.getLocalizedMessage());
1211: parseErrors.add(new DefaultCssSyntaxErrorInfo(cpe
1212: .getLineNumber(), cpe.getLocalizedMessage()));
1213: }
1214: } // End of DefaultErrorHandler.
1215:
1216: private static class DefaultCssSyntaxErrorInfo implements
1217: CssSyntaxErrorInfo {
1218: private final int lineNumber;
1219: private final String localizedMessage;
1220:
1221: public DefaultCssSyntaxErrorInfo(int lineNumber,
1222: String localizeMessage) {
1223: this .lineNumber = lineNumber;
1224: this .localizedMessage = localizeMessage;
1225: }
1226:
1227: public int getLineNumber() {
1228: return lineNumber;
1229: }
1230:
1231: public String getLocalizedMessage() {
1232: return localizedMessage;
1233: }
1234: } // End of DefaultCssSyntaxErrorInfo.
1235:
1236: private static class CssEngineDataHandler implements
1237: UserDataHandler {
1238: private static final CssEngineDataHandler INSTANCE = new CssEngineDataHandler();
1239:
1240: public static CssEngineDataHandler getDefault() {
1241: return INSTANCE;
1242: }
1243:
1244: public void handle(short operation, String key, Object data,
1245: Node src, Node dst) {
1246: // No op.
1247: }
1248: } // End of CssEngineDataHandler.
1249:
1250: }
|