blob: db251dd05a1b25fb97a7be58b1d23ce8f5d59345 [file] [log] [blame]
/*******************************************************************************
* 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.HashMap;
import java.util.Map;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.jst.pagedesigner.IHTMLConstants;
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.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;
/**
* @return the element this style if for
*/
public Element getElement() {
return _element;
}
/**
* @param element
*/
public AbstractStyle(Element element) {
_element = element;
}
/**
* 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");
if (name == null || name.length() == 0) {
name = getHtmlElement().getAttribute("name");
}
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)+ARTIFICIAL_BORDER_OFFSET;
int left = getInsetProperty(ICSSPropertyID.ATTR_PADDING_LEFT)+ARTIFICIAL_BORDER_OFFSET;
int bottom = getInsetProperty(ICSSPropertyID.ATTR_PADDING_BOTTOM)+ARTIFICIAL_BORDER_OFFSET;
int right = getInsetProperty(ICSSPropertyID.ATTR_PADDING_RIGHT)+ARTIFICIAL_BORDER_OFFSET;
_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)) {
return false;
}
String tag = this.getHTMLTag();
if ("img".equalsIgnoreCase(tag)) {
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");
buffer.append(_cache.getCssText()).append("\n");
}
if (_defaultCache != null) {
buffer.append("default cache:\n");
buffer.append(_defaultCache.getCssText()).append("\n");
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jst.pagedesigner.css2.ICSSStyle#getColSpan()
*/
public int getColSpan() {
int colspan = DOMUtil.getIntAttributeIgnoreCase(getHtmlElement(),
"colspan", 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);
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);
}
}