/******************************************************************************* * Copyright (c) 2006 Sybase, Inc. and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Sybase, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.jst.pagedesigner.css2.style; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.eclipse.draw2d.geometry.Insets; import org.eclipse.jst.pagedesigner.IHTMLConstants; import org.eclipse.jst.pagedesigner.converter.ITagConverter; import org.eclipse.jst.pagedesigner.css2.CSSUtil; import org.eclipse.jst.pagedesigner.css2.ICSSStyle; import org.eclipse.jst.pagedesigner.css2.font.CSSFontManager; import org.eclipse.jst.pagedesigner.css2.font.ICSSFont; import org.eclipse.jst.pagedesigner.css2.font.ICSSFontManager; import org.eclipse.jst.pagedesigner.css2.list.CounterHelper; import org.eclipse.jst.pagedesigner.css2.list.CounterValueGenerator; import org.eclipse.jst.pagedesigner.css2.list.ICounterValueGenerator; import org.eclipse.jst.pagedesigner.css2.property.CSSMetaRegistry; import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyMeta; import org.eclipse.jst.pagedesigner.css2.value.Length; import org.eclipse.jst.pagedesigner.ui.preferences.PDPreferences; import org.eclipse.jst.pagedesigner.utils.DOMUtil; import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.css.CSSStyleDeclaration; import org.w3c.dom.css.CSSValue; /** * @author mengbo */ public class AbstractStyle implements ICSSStyle { private final Element _element; private Map _cachedValues = new HashMap(); private ICSSFont _font = null; private CSSStyleDeclaration _cache; private CSSStyleDeclaration _defaultCache; private boolean _cssDeclareWasSearched = false; private boolean _cssDefaultDeclareWasSearched = false; private Insets _borderInsets, _marginInsets, _paddingInsets; private ICSSStyle _parentStyle; private HashMap _counters = null; private final PDPreferences _prefs; /** * @return the element this style if for */ public Element getElement() { return _element; } /** * @param element * @param prefs */ public AbstractStyle(Element element, PDPreferences prefs) { _element = element; _prefs = prefs; } /** * reset all the cache. */ public void reset() { _cachedValues.clear(); _font = null; _cache = null; _defaultCache = null; _cssDeclareWasSearched = false; _cssDefaultDeclareWasSearched = false; // if (_counters != null) // { // unregistCounter(); // _counters.clear(); // _counters = null; // } _borderInsets = _marginInsets = _paddingInsets = null; } /** * this is a hook method so caller can use it to override default * calculation. Note, after the call to <code>reset</code>, it will be * lost. * * @param property * @param value */ public void setStyleProperty(String property, Object value) { _cachedValues.put(property, value); } /** * get a style property value. * * @param property * @return the style property */ public Object getStyleProperty(String property) { Object value = _cachedValues.get(property); if (value == null) { value = calculateProperty(property); if (value != null) { _cachedValues.put(property, value); } } return value; } /** * in this method, should first check the "style" attribute, then combine * that with document style. * * @return the style */ protected CSSStyleDeclaration calculateDeclaration() { String name = getHtmlElement().getAttribute("id"); //$NON-NLS-1$ if (name == null || name.length() == 0) { name = getHtmlElement().getAttribute("name"); //$NON-NLS-1$ } return CSSUtil.getCSSDeclaration(this.getHtmlElement(), name); } /** * @return the style */ protected CSSStyleDeclaration calculateDefaultDeclaration() { return CSSUtil.getDefaultCSSDeclaration(this.getHtmlElement(), null); } /** * @return the style declaration */ public CSSStyleDeclaration getDeclaration() { // FIXME:may need to be change, boolean variable is not a best way. if (!_cssDeclareWasSearched) { _cache = calculateDeclaration(); _cssDeclareWasSearched = true; } return _cache; } /** * @return the default declaration */ public CSSStyleDeclaration getDefaultDeclaration() { // FIXME:may need to be change, boolean variable is not a best way. if (!_cssDefaultDeclareWasSearched) { _defaultCache = calculateDefaultDeclaration(); _cssDefaultDeclareWasSearched = true; } return _defaultCache; } /* (non-Javadoc) * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getHTMLelementInitValue(java.lang.String) */ public Object getHTMLelementInitValue(String propertyName) { ICSSPropertyMeta meta = getPropertyMeta(propertyName); if (meta != null) { Object obj = meta.getHTMLElementInitialValue(_element, getHTMLTag(), propertyName); if (obj == null) { obj = meta.getInitialValue(propertyName, this); } return obj; } return ICSSPropertyMeta.NOT_SPECIFIED; } /** * @param propertyName * @return the property */ protected Object calculateProperty(String propertyName) { ICSSPropertyMeta meta = getPropertyMeta(propertyName); Object result = null; // get declaration CSSStyleDeclaration decl = getDeclaration(); CSSValue value = decl == null ? null : decl .getPropertyCSSValue(propertyName); if (value == null) { if (meta != null) { result = meta.calculateHTMLAttributeOverride(_element, getHTMLTag(), propertyName, this); if (result != null) { return result; } } decl = getDefaultDeclaration(); } value = decl == null ? null : decl.getPropertyCSSValue(propertyName); if (value != null && value.getCssValueType() == CSSValue.CSS_INHERIT) { result = getParentResultValue(meta, propertyName); } else if (value == null) { if (meta != null) { result = meta.calculateHTMLAttributeOverride(_element, getHTMLTag(), propertyName, this); } if (result == null) { result = calculateLocalOverride(meta, propertyName); } if (result == null) { if (meta == null) { result = ICSSPropertyMeta.NOT_SPECIFIED; } else { if (meta.isInherited()) { result = getParentResultValue(meta, propertyName); } else { result = meta.getInitialValue(propertyName, this); } } } } else { result = calculateCSSValueResult(meta, value, propertyName); } return result; } /** * get the corresponding HTML tag for this style. This is for certain HTML * tag can also provide style information. * * @return the html tag */ protected String getHTMLTag() { return _element.getTagName(); } /** * @param propertyName * @return the property meta for property name */ protected ICSSPropertyMeta getPropertyMeta(String propertyName) { return CSSMetaRegistry.getInstance().getMeta(propertyName); } /** * convert the CSSValue to the property type specified data result. * * @param meta * @param value * @param propertyName * @return should not return null. */ protected Object calculateCSSValueResult(ICSSPropertyMeta meta, CSSValue value, String propertyName) { if (meta == null) { return ICSSPropertyMeta.NOT_SPECIFIED; } return meta.calculateCSSValueResult(value, propertyName, this); } /** * it is possible that some attribute of the element may provide style * information. child class should override this method. Also, some element * type may have internally build style, such as input-submit may use * special border. NOTE: it is very important that in calculateLocalOverride * you don't directly or indirectly call getStyleProperty() to avoid * deadloop. * * @param meta * @param propertyName * @return null means no style information in other attributes. Otherwise * return property specific data result -- normally will use meta to * convert the attribute. */ protected Object calculateLocalOverride(ICSSPropertyMeta meta, String propertyName) { // // do some default margin thing. // if (ICSSPropertyID.ATTR_MARGIN_RIGHT.equalsIgnoreCase(propertyName) // || ICSSPropertyID.ATTR_MARGIN_BOTTOM.equalsIgnoreCase(propertyName)) // { // return MARGIN_LENGTH; // } // else if // (ICSSPropertyID.ATTR_MARGIN_LEFT.equalsIgnoreCase(propertyName)) // { // // to make a little room, so it is possible for user to position the // // mouse before the first element in a block. // return MARGIN_LEFT; // } return null; } /** * This is only called when inherit value from parent. * * @param meta * @param propertyName * @return the result value */ protected Object getParentResultValue(ICSSPropertyMeta meta, String propertyName) { ICSSStyle style = getParentStyle(); return style.getStyleProperty(propertyName); } /** * @param parentStyle */ public void setParentStyle(ICSSStyle parentStyle) { this._parentStyle = parentStyle; reset(); } public ICSSStyle getParentStyle() { if (_parentStyle != null) { return _parentStyle; } Node node = _element.getParentNode(); while (node instanceof Element && node instanceof INodeNotifier) { ICSSStyle parentStyle = (ICSSStyle) ((INodeNotifier) node) .getAdapterFor(ICSSStyle.class); if (parentStyle != null) { return parentStyle; } node = node.getParentNode(); } return DefaultStyle.getInstance(); } /** * Will not return null * * @return the font */ public ICSSFont getCSSFont() { if (_font == null) { _font = getFontManager().createFont(this); } return _font; } /** * @return */ private ICSSFontManager getFontManager() { return CSSFontManager.getInstance(); } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#dispose() */ public void dispose() { // TODO: anything to dispose? } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getMarginInsets() */ public Insets getMarginInsets() { if (_marginInsets == null) { int top = getInsetProperty(ICSSPropertyID.ATTR_MARGIN_TOP); int left = getInsetProperty(ICSSPropertyID.ATTR_MARGIN_LEFT); int bottom = getInsetProperty(ICSSPropertyID.ATTR_MARGIN_BOTTOM); int right = getInsetProperty(ICSSPropertyID.ATTR_MARGIN_RIGHT); _marginInsets = new Insets(top, left, bottom, right); } return _marginInsets; } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getPaddingInsets() */ public Insets getPaddingInsets() { if (_paddingInsets == null) { int top = getInsetProperty(ICSSPropertyID.ATTR_PADDING_TOP); int left = getInsetProperty(ICSSPropertyID.ATTR_PADDING_LEFT); int bottom = getInsetProperty(ICSSPropertyID.ATTR_PADDING_BOTTOM); int right = getInsetProperty(ICSSPropertyID.ATTR_PADDING_RIGHT); //add extra padding only for the top element of a source tag's rendering if (elementIsTagConverted()) { final int borderOffset = _prefs.getCssArtificialCellPadding(); top += borderOffset; left += borderOffset; bottom += borderOffset; right += borderOffset; } _paddingInsets = new Insets(top, left, bottom, right); } return _paddingInsets; } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getBorderInsets() */ public Insets getBorderInsets() { if (_borderInsets == null) { int top = getInsetProperty(ICSSPropertyID.ATTR_BORDER_TOP_WIDTH); int left = getInsetProperty(ICSSPropertyID.ATTR_BORDER_LEFT_WIDTH); int bottom = getInsetProperty(ICSSPropertyID.ATTR_BORDER_BOTTOM_WIDTH); int right = getInsetProperty(ICSSPropertyID.ATTR_BORDER_RIGHT_WIDTH); _borderInsets = new Insets(top, left, bottom, right); } return _borderInsets; } /** * @param border_top_width * @return */ private int getInsetProperty(String propertyName) { Object obj = this.getStyleProperty(propertyName); if (obj instanceof Length) { Length l = (Length) obj; if (l.isPercentage()) { return 0; // FIXME: } return l.getValue(); } return 0; } public boolean isAdapterForType(Object type) { return (type == ICSSStyle.class); } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#isSizeIncludeBorderPadding() */ public boolean isSizeIncludeBorderPadding() { String display = this.getDisplay(); if ("table-cell".equalsIgnoreCase(display)) { //$NON-NLS-1$ return false; } String tag = this.getHTMLTag(); if ("img".equalsIgnoreCase(tag)) { //$NON-NLS-1$ return false; } return true; } /* * (non-Javadoc) * * @see com.ibm.sse.model.INodeAdapter#notifyChanged(com.ibm.sse.model.INodeNotifier, * int, java.lang.Object, java.lang.Object, java.lang.Object, int) */ public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { // do nothing } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getBackgroundColor() */ public Object getColor() { Object _color = null; if (_color == null) { _color = getStyleProperty(ICSSPropertyID.ATTR_COLOR); if (_color == null) { _color = getStyleProperty(ICSSPropertyID.ATTR_TEXTCOLOR); } } return _color; } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getColor() */ public Object getBackgroundColor() { Object _backgroundColor = null; if (_backgroundColor == null) { _backgroundColor = getStyleProperty(ICSSPropertyID.ATTR_BACKGROUND_COLOR); } return _backgroundColor; } /** * @return the html element */ public Element getHtmlElement() { // if (_element instanceof IDOMElement) // { // EditPart part = (EditPart) ((IDOMElement) // _element).getAdapterFor(EditPart.class); // if (part instanceof ElementEditPart) // { // ElementEditPart elementPart = (ElementEditPart) part; // ITagHandler h = elementPart.getTagHandler(); // if (h != null) // { // return h.mapCustomElement(_element); // } // } // // } return _element; } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getDisplay() */ public String getDisplay() { Object display = this.getStyleProperty(ICSSPropertyID.ATTR_DISPLAY); String displayStr; if (display == null) { displayStr = ICSSPropertyID.VAL_INLINE; } else if (display instanceof String) { displayStr = (String) display; } else { displayStr = display.toString(); } if (ICSSPropertyID.VAL_INLINE.equalsIgnoreCase(displayStr) && IHTMLConstants.TAG_TABLE.equalsIgnoreCase(getHTMLTag())) { return ICSSPropertyID.VAL_INLINE_TABLE; } if (ICSSPropertyID.VAL_INLINE.equalsIgnoreCase(displayStr)) { Object width = this.getStyleProperty(ICSSPropertyID.ATTR_WIDTH); if (width instanceof Length) { return ICSSPropertyID.VAL_INLINE_BLOCK; } Object height = this.getStyleProperty(ICSSPropertyID.ATTR_HEIGHT); if (height instanceof Length) { return ICSSPropertyID.VAL_INLINE_BLOCK; } return displayStr; } return displayStr; } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getListStyle() */ public Map getCounters() { if (_counters == null) { _counters = new HashMap(); CounterHelper.processCounterReset(this, _counters); } return _counters; } /** * Get named counter from counters. * * see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getCounter(java.lang.String) */ public ICounterValueGenerator findCounter(String name, boolean must) { Map counters = getCounters(); if (counters == null || !counters.containsKey(name)) { if (getParentStyle() != null && !(getParentStyle() instanceof DefaultStyle)) { // ensure it is registered somewhere. return getParentStyle().findCounter(name, must); } // must is called by counter-increment else if (must) { // the caller should do the other setting. ICounterValueGenerator counter = new CounterValueGenerator( name, null, null, this); counter.resetCount(); counters.put(name, counter); } } return (ICounterValueGenerator) counters.get(name); } /** * @param buffer */ public void dumpDebugInfo(StringBuffer buffer) { if (_cache != null) { buffer.append("cache:\n"); //$NON-NLS-1$ buffer.append(_cache.getCssText()).append("\n"); //$NON-NLS-1$ } if (_defaultCache != null) { buffer.append("default cache:\n"); //$NON-NLS-1$ buffer.append(_defaultCache.getCssText()).append("\n"); //$NON-NLS-1$ } } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getColSpan() */ public int getColSpan() { int colspan = DOMUtil.getIntAttributeIgnoreCase(getHtmlElement(), "colspan", 1); //$NON-NLS-1$ // if span == 0, means span all col from the current column to end // colume if (colspan < 0) { return 1; } return colspan; } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getRowSpan() */ public int getRowSpan() { int rowspan = DOMUtil.getIntAttributeIgnoreCase(getHtmlElement(), "rowspan", 1); //$NON-NLS-1$ if (rowspan < 0) { return 1; } return rowspan; } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#isInSelection() */ public boolean isInSelection() { IRangeSelectionProxy proxy = (IRangeSelectionProxy) getAdapter(IRangeSelectionProxy.class); if (proxy != null) { return proxy.isRangeSelected(); } ICSSStyle parentStyle = getParentStyle(); if (parentStyle != null) { return parentStyle.isInSelection(); } return false; } /* * (non-Javadoc) * * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ public Object getAdapter(Class adapter) { if (this._element instanceof INodeNotifier) { Object ret = ((INodeNotifier) _element).getAdapterFor(adapter); if (ret != null && adapter.isInstance(ret)) { return ret; } } return null; } // private void unregistCounter() // { // if (_counters != null) // { // Collection c = _counters.values(); // Iterator iter = c.iterator(); // while (iter.hasNext()) // { // Counter2 counter = (Counter2) iter.next(); // counter.unregist(this); // } // } // } /* * (non-Javadoc) * * @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#resetCounters() */ public void processCounters() { this._counters = null; CounterHelper.processCounterIncrement(this); } private boolean elementIsTagConverted() { boolean isTagConverted = false; if (_element instanceof INodeNotifier) { Collection nodeAdapters = ((INodeNotifier)_element).getAdapters(); for (Object nodeAdapter: nodeAdapters) { if (nodeAdapter instanceof ITagConverter) { isTagConverted = true; break; } } } return isTagConverted; } }