| /******************************************************************************* |
| * 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.converter; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.jst.pagedesigner.css2.style.ITagEditInfo; |
| import org.eclipse.jst.pagedesigner.dom.DOMUtil; |
| import org.eclipse.jst.pagedesigner.preview.PageExpressionContext; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter; |
| import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.Text; |
| |
| /** |
| * This is base class for all non-hidden tag converters. |
| * |
| * @author mengbo |
| * @version 1.5 |
| */ |
| public abstract class AbstractTagConverter implements ITagConverter, |
| ITagEditInfo, INodeAdapter, IDOMFactory { |
| private IDOMDocument _targetDocument; |
| |
| private Element _hostElement; |
| |
| private Element _resultElement; |
| |
| private List _childNodes = Collections.EMPTY_LIST; |
| |
| private Map _childNodePositions = Collections.EMPTY_MAP; |
| |
| private int _mode; |
| |
| private int _minWidth; |
| |
| private int _minHeight; |
| |
| private boolean _needBorderDecorator; |
| |
| /** |
| * @param host |
| * |
| */ |
| public AbstractTagConverter(Element host) { |
| _hostElement = host; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.converter.ITagConverter#setTargetDocument(org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument) |
| */ |
| public void setDestDocument(IDOMDocument document) { |
| _targetDocument = document; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.visualtag.ITagConverter#convertRefresh(java.lang.Object) |
| */ |
| public final void convertRefresh(Object context) { |
| _resultElement = null; |
| _childNodes = new ArrayList(); |
| _childNodePositions = new HashMap(); |
| |
| _resultElement = doConvertRefresh(); |
| if (_resultElement instanceof INodeNotifier) { |
| ((INodeNotifier) _resultElement).addAdapter(this); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.wst.sse.core.internal.provisional.INodeAdapter#notifyChanged(org.eclipse.wst.sse.core.internal.provisional.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. |
| } |
| |
| /** |
| * Child class should override this method. The child class should NEVER |
| * change the host DOM structure. |
| * |
| * @return the convert result. Should be an HTML element. |
| */ |
| protected abstract Element doConvertRefresh(); |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.visualtag.ITagConverter#getHostElement() |
| */ |
| public final Element getHostElement() { |
| return _hostElement; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.visualtag.ITagConverter#getResultElement() |
| */ |
| public final Element getResultElement() { |
| return _resultElement; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.visualtag.ITagConverter#getChildModeList() |
| */ |
| public final List getChildModeList() { |
| return _childNodes; |
| } |
| |
| |
| public List getNonVisualChildren() |
| { |
| // by default, no non-visual children |
| return Collections.EMPTY_LIST; |
| } |
| |
| /** |
| * child class should call this method. |
| * |
| * @param childNode |
| * the childNode of the hostElement that should be futher |
| * converted. |
| * @param position |
| * |
| */ |
| protected void addChild(Node childNode, ConvertPosition position) { |
| _childNodes.add(childNode); |
| _childNodePositions.put(childNode, position); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.visualtag.ITagConverter#getChildVisualPosition(org.w3c.dom.Node) |
| */ |
| public final ConvertPosition getChildVisualPosition(Node childModel) { |
| return (ConvertPosition) _childNodePositions.get(childModel); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.visualtag.ITagConverter#isVisualByHTML() |
| */ |
| public boolean isVisualByHTML() { |
| return true; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.visualtag.ITagConverter#getVisualImage() |
| */ |
| public Image getVisualImage() { |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.visualtag.ITagConverter#dispose() |
| */ |
| public void dispose() { |
| // do nothing; children may wish to sub-class |
| // TODO: null shared references? |
| // this doesn't seem to be called by anybody.. |
| // need to review this |
| } |
| |
| /** |
| * @param node |
| * @return true if the node should be ignored for conversion purposes |
| */ |
| protected boolean shouldIgnore(Node node) { |
| int nodeType = node.getNodeType(); |
| switch (nodeType) { |
| case Node.TEXT_NODE: |
| case Node.CDATA_SECTION_NODE: |
| case Node.ELEMENT_NODE: |
| return false; |
| default: |
| return true; |
| } |
| |
| } |
| |
| /** |
| * utility method for those converter that only converts the host tag's name |
| * and directly copy children. |
| * @param src |
| * @param dest |
| * |
| */ |
| protected void copyChildren(Element src, Element dest) { |
| Node node = src.getFirstChild(); |
| int index = 0; |
| for (; node != null; node = node.getNextSibling()) { |
| if (!shouldIgnore(node)) { |
| addChild(node, new ConvertPosition(dest, index++)); |
| } |
| } |
| } |
| |
| /** |
| * utility method for those converter that directly copy children. |
| * @param src |
| * @param dest |
| * |
| */ |
| protected void dumCopyChildren(Element src, Element dest) { |
| Node node = src.getFirstChild(); |
| Document destDoc = dest.getOwnerDocument(); |
| for (; node != null; node = node.getNextSibling()) { |
| if (!shouldIgnore(node)) { |
| Node n = DOMUtil.cloneNodeDeepIgnoreError(destDoc, node); |
| dest.appendChild(n); |
| } |
| } |
| } |
| |
| /** |
| * In the future, the conversion result HTML DOM tree could be in another |
| * document. |
| * |
| * @return the destination document |
| */ |
| public IDOMDocument getDestDocument() { |
| if (this._targetDocument != null) { |
| return this._targetDocument; |
| } |
| return (IDOMDocument) _hostElement.getOwnerDocument(); |
| } |
| |
| /** |
| * shortcut method. Child class should always use this method to create a |
| * result element. |
| * |
| * @param tagName |
| * @return a new element named tagName |
| */ |
| public Element createElement(String tagName) { |
| return getDestDocument().createElement(tagName); |
| } |
| |
| /** |
| * shortcut method. Child class should always use this method to create a |
| * text node. |
| * |
| * @param text |
| * @return a new text node using text as the value |
| */ |
| public Text createText(String text) { |
| return getDestDocument().createTextNode(text); |
| } |
| |
| /** |
| * @param original |
| * @return the mapped String TODO: currently does nothing |
| */ |
| protected String mapURL(String original) { |
| // TODO: how to map URL? such as original url look like: |
| // getContext().getPath()+... |
| return original; |
| } |
| |
| // TODO: FIXME: XXX: |
| // if the value is expression, we may want to do something here!!! |
| /** |
| * @param value |
| * @return value mapped based on EL expression |
| */ |
| protected String mapValue(String value) { |
| if (value == null) { |
| return null; |
| } |
| if (isDesignerMode()) { |
| // if there has jsf binding expressions |
| int checkPos = value.indexOf("#{"); //$NON-NLS-1$ |
| if (checkPos != -1) { |
| String mapValue = ""; //$NON-NLS-1$ |
| int preferType = PreferenceReader.getMapValueType(); |
| switch (preferType) { |
| case PreferenceReader.FULL_EXPRESSION_TYPE: |
| mapValue = value; |
| break; |
| case PreferenceReader.LAST_EXPRESSION_TYPE: |
| String strBackup = value; |
| StringBuffer sb = new StringBuffer(); |
| while (strBackup.indexOf("#{") != -1) { //$NON-NLS-1$ |
| int pos = strBackup.indexOf("#{"); //$NON-NLS-1$ |
| int endBracketPos = strBackup.indexOf("}", pos + 1); //$NON-NLS-1$ |
| if (endBracketPos != -1) { |
| sb.append(strBackup.substring(0, pos + 2)); |
| String exp = strBackup.substring(pos + 2, |
| endBracketPos); |
| if (allowTrim(exp)) { |
| int lastDotPos = exp.lastIndexOf("."); //$NON-NLS-1$ |
| if (lastDotPos != -1) { |
| String convertedExp = exp |
| .substring(lastDotPos + 1); |
| sb.append(convertedExp); |
| } else { |
| sb.append(exp); |
| } |
| |
| } else { |
| sb.append(exp); |
| } |
| sb.append("}"); //$NON-NLS-1$ |
| } else { |
| break; |
| } |
| if (strBackup.length() > endBracketPos + 1) { |
| strBackup = strBackup.substring(endBracketPos + 1); |
| } else { |
| strBackup = ""; //$NON-NLS-1$ |
| break; |
| } |
| |
| } |
| sb.append(strBackup); |
| mapValue = sb.toString(); |
| break; |
| case PreferenceReader.REAL_VALUE_TYPE: |
| // TODO calculate the expression value |
| default: |
| mapValue = value; |
| break; |
| } |
| |
| return mapValue; |
| } |
| } else { |
| // preview mode. let's try to display the value. |
| try { |
| return (String) PageExpressionContext.getCurrent() |
| .evaluateExpression(value, String.class, null); |
| } catch (Exception ex) { |
| // can't calculate the result. ignore. |
| // ex.printStackTrace(); |
| } |
| } |
| return value; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.css2.style.ITagEditInfo#needBorderDecorator() |
| */ |
| public boolean needBorderDecorator() { |
| return this._needBorderDecorator; |
| } |
| |
| /** |
| * @param b |
| */ |
| public void setNeedBorderDecorator(boolean b) { |
| this._needBorderDecorator = b; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.css2.style.ITagEditInfo#needTableDecorator() |
| */ |
| public boolean needTableDecorator() { |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.wst.sse.core.internal.provisional.INodeAdapter#isAdapterForType(java.lang.Object) |
| */ |
| public boolean isAdapterForType(Object type) { |
| if (type == ITagEditInfo.class) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @param mode |
| */ |
| public final void setMode(int mode) { |
| this._mode = mode; |
| } |
| |
| /** |
| * @return true if the converter mode is preview |
| */ |
| public final boolean isPreviewMode() { |
| return this._mode == IConverterFactory.MODE_PREVIEW; |
| } |
| |
| /** |
| * @return true if the converter mode is designer |
| */ |
| public final boolean isDesignerMode() { |
| return this._mode == IConverterFactory.MODE_DESIGNER; |
| } |
| |
| /** |
| * @return the converter mode |
| */ |
| public final int getMode() { |
| return this._mode; |
| } |
| |
| /** |
| * The method is used to judge whether the value binding and method binding |
| * expression is allowed to be trimmed.Currently only expression contains |
| * only letter,digit,and '.' is allowed to be trimmed. |
| * |
| * @param expression |
| * value binding or method binding expression |
| * @return |
| */ |
| private boolean allowTrim(String expression) { |
| for (int i = 0, size = expression.length(); i < size; i++) { |
| char ch = expression.charAt(i); |
| if (!Character.isLetterOrDigit(ch) && (ch != '.') && (ch != '_')) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.converter.AbstractTagConverter#getMinWidth() |
| */ |
| public int getMinWidth() { |
| return this._minWidth; |
| } |
| |
| /** |
| * @param minWidth |
| */ |
| public void setMinWidth(int minWidth) { |
| this._minWidth = minWidth; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.converter.AbstractTagConverter#getMinHeight() |
| */ |
| public int getMinHeight() { |
| return this._minHeight; |
| } |
| |
| /** |
| * @param minHeight |
| */ |
| public void setMinHeight(int minHeight) { |
| this._minHeight = minHeight; |
| } |
| |
| /** |
| * @param element |
| * @param attrname |
| * @return the attribute on element with the name attrname |
| */ |
| public static boolean hasAttribute(Element element, String attrname) { |
| return element.hasAttribute(attrname); |
| } |
| } |