diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java | 661 |
1 files changed, 0 insertions, 661 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java deleted file mode 100644 index 858156884..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.layout; - -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_LAYOUT; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_PADDING; -import static com.android.SdkConstants.AUTO_URI; -import static com.android.SdkConstants.UNIT_DIP; -import static com.android.SdkConstants.UNIT_DP; -import static com.android.SdkConstants.UNIT_IN; -import static com.android.SdkConstants.UNIT_MM; -import static com.android.SdkConstants.UNIT_PT; -import static com.android.SdkConstants.UNIT_PX; -import static com.android.SdkConstants.UNIT_SP; -import static com.android.SdkConstants.VALUE_FILL_PARENT; -import static com.android.SdkConstants.VALUE_MATCH_PARENT; -import static com.android.SdkConstants.VIEW_FRAGMENT; -import static com.android.SdkConstants.VIEW_INCLUDE; - -import com.android.ide.common.rendering.api.ILayoutPullParser; -import com.android.ide.common.rendering.api.ViewInfo; -import com.android.ide.common.res2.ValueXmlHelper; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.FragmentMenu; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.resources.Density; -import com.android.sdklib.IAndroidTarget; - -import org.eclipse.core.resources.IProject; -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.xmlpull.v1.XmlPullParserException; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * {@link ILayoutPullParser} implementation on top of {@link UiElementNode}. - * <p/> - * It's designed to work on layout files, and will most likely not work on other resource files. - * <p/> - * This pull parser generates {@link ViewInfo}s which key is a {@link UiElementNode}. - */ -public class UiElementPullParser extends BasePullParser { - private final static Pattern FLOAT_PATTERN = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)"); //$NON-NLS-1$ - - private final int[] sIntOut = new int[1]; - - private final ArrayList<UiElementNode> mNodeStack = new ArrayList<UiElementNode>(); - private UiElementNode mRoot; - private final boolean mExplodedRendering; - private boolean mZeroAttributeIsPadding = false; - private boolean mIncreaseExistingPadding = false; - private LayoutDescriptors mDescriptors; - private final Density mDensity; - - /** - * Number of pixels to pad views with in exploded-rendering mode. - */ - private static final String DEFAULT_PADDING_VALUE = - ExplodedRenderingHelper.PADDING_VALUE + UNIT_PX; - - /** - * Number of pixels to pad exploded individual views with. (This is HALF the width of the - * rectangle since padding is repeated on both sides of the empty content.) - */ - private static final String FIXED_PADDING_VALUE = "20px"; //$NON-NLS-1$ - - /** - * Set of nodes that we want to auto-pad using {@link #FIXED_PADDING_VALUE} as the padding - * attribute value. Can be null, which is the case when we don't want to perform any - * <b>individual</b> node exploding. - */ - private final Set<UiElementNode> mExplodeNodes; - - /** - * Constructs a new {@link UiElementPullParser}, a parser dedicated to the special case of - * parsing a layout resource files, and handling "exploded rendering" - adding padding on views - * to make them easier to see and operate on. - * - * @param top The {@link UiElementNode} for the root node. - * @param explodeRendering When true, add padding to <b>all</b> nodes in the hierarchy. This - * will add rather than replace padding of a node. - * @param explodeNodes A set of individual nodes that should be assigned a fixed amount of - * padding ({@link #FIXED_PADDING_VALUE}). This is intended for use with nodes that - * (without padding) would be invisible. This parameter can be null, in which case - * nodes are not individually exploded (but they may all be exploded with the - * explodeRendering parameter. - * @param density the density factor for the screen. - * @param project Project containing this layout. - */ - public UiElementPullParser(UiElementNode top, boolean explodeRendering, - Set<UiElementNode> explodeNodes, - Density density, IProject project) { - super(); - mRoot = top; - mExplodedRendering = explodeRendering; - mExplodeNodes = explodeNodes; - mDensity = density; - if (mExplodedRendering) { - // get the layout descriptor - IAndroidTarget target = Sdk.getCurrent().getTarget(project); - AndroidTargetData data = Sdk.getCurrent().getTargetData(target); - mDescriptors = data.getLayoutDescriptors(); - } - push(mRoot); - } - - protected UiElementNode getCurrentNode() { - if (mNodeStack.size() > 0) { - return mNodeStack.get(mNodeStack.size()-1); - } - - return null; - } - - private Node getAttribute(int i) { - if (mParsingState != START_TAG) { - throw new IndexOutOfBoundsException(); - } - - // get the current uiNode - UiElementNode uiNode = getCurrentNode(); - - // get its xml node - Node xmlNode = uiNode.getXmlNode(); - - if (xmlNode != null) { - return xmlNode.getAttributes().item(i); - } - - return null; - } - - private void push(UiElementNode node) { - mNodeStack.add(node); - - mZeroAttributeIsPadding = false; - mIncreaseExistingPadding = false; - - if (mExplodedRendering) { - // first get the node name - String xml = node.getDescriptor().getXmlLocalName(); - ViewElementDescriptor descriptor = mDescriptors.findDescriptorByTag(xml); - if (descriptor != null) { - NamedNodeMap attributes = node.getXmlNode().getAttributes(); - Node padding = attributes.getNamedItemNS(ANDROID_URI, ATTR_PADDING); - if (padding == null) { - // we'll return an extra padding - mZeroAttributeIsPadding = true; - } else { - mIncreaseExistingPadding = true; - } - } - } - } - - private UiElementNode pop() { - return mNodeStack.remove(mNodeStack.size()-1); - } - - // ------------- IXmlPullParser -------- - - /** - * {@inheritDoc} - * <p/> - * This implementation returns the underlying DOM node of type {@link UiElementNode}. - * Note that the link between the GLE and the parsing code depends on this being the actual - * type returned, so you can't just randomly change it here. - * <p/> - * Currently used by: - * - private method GraphicalLayoutEditor#updateNodeWithBounds(ILayoutViewInfo). - * - private constructor of LayoutCanvas.CanvasViewInfo. - */ - @Override - public Object getViewCookie() { - return getCurrentNode(); - } - - /** - * Legacy method required by {@link com.android.layoutlib.api.IXmlPullParser} - */ - @Override - public Object getViewKey() { - return getViewCookie(); - } - - /** - * This implementation does nothing for now as all the embedded XML will use a normal KXML - * parser. - */ - @Override - public ILayoutPullParser getParser(String layoutName) { - return null; - } - - // ------------- XmlPullParser -------- - - @Override - public String getPositionDescription() { - return "XML DOM element depth:" + mNodeStack.size(); - } - - /* - * This does not seem to be called by the layoutlib, but we keep this (and maintain - * it) just in case. - */ - @Override - public int getAttributeCount() { - UiElementNode node = getCurrentNode(); - - if (node != null) { - Collection<UiAttributeNode> attributes = node.getAllUiAttributes(); - int count = attributes.size(); - - return count + (mZeroAttributeIsPadding ? 1 : 0); - } - - return 0; - } - - /* - * This does not seem to be called by the layoutlib, but we keep this (and maintain - * it) just in case. - */ - @Override - public String getAttributeName(int i) { - if (mZeroAttributeIsPadding) { - if (i == 0) { - return ATTR_PADDING; - } else { - i--; - } - } - - Node attribute = getAttribute(i); - if (attribute != null) { - return attribute.getLocalName(); - } - - return null; - } - - /* - * This does not seem to be called by the layoutlib, but we keep this (and maintain - * it) just in case. - */ - @Override - public String getAttributeNamespace(int i) { - if (mZeroAttributeIsPadding) { - if (i == 0) { - return ANDROID_URI; - } else { - i--; - } - } - - Node attribute = getAttribute(i); - if (attribute != null) { - return attribute.getNamespaceURI(); - } - return ""; //$NON-NLS-1$ - } - - /* - * This does not seem to be called by the layoutlib, but we keep this (and maintain - * it) just in case. - */ - @Override - public String getAttributePrefix(int i) { - if (mZeroAttributeIsPadding) { - if (i == 0) { - // figure out the prefix associated with the android namespace. - Document doc = mRoot.getXmlDocument(); - return doc.lookupPrefix(ANDROID_URI); - } else { - i--; - } - } - - Node attribute = getAttribute(i); - if (attribute != null) { - return attribute.getPrefix(); - } - return null; - } - - /* - * This does not seem to be called by the layoutlib, but we keep this (and maintain - * it) just in case. - */ - @Override - public String getAttributeValue(int i) { - if (mZeroAttributeIsPadding) { - if (i == 0) { - return DEFAULT_PADDING_VALUE; - } else { - i--; - } - } - - Node attribute = getAttribute(i); - if (attribute != null) { - String value = attribute.getNodeValue(); - if (mIncreaseExistingPadding && ATTR_PADDING.equals(attribute.getLocalName()) && - ANDROID_URI.equals(attribute.getNamespaceURI())) { - // add the padding and return the value - return addPaddingToValue(value); - } - return value; - } - - return null; - } - - /* - * This is the main method used by the LayoutInflater to query for attributes. - */ - @Override - public String getAttributeValue(String namespace, String localName) { - if (mExplodeNodes != null && ATTR_PADDING.equals(localName) && - ANDROID_URI.equals(namespace)) { - UiElementNode node = getCurrentNode(); - if (node != null && mExplodeNodes.contains(node)) { - return FIXED_PADDING_VALUE; - } - } - - if (mZeroAttributeIsPadding && ATTR_PADDING.equals(localName) && - ANDROID_URI.equals(namespace)) { - return DEFAULT_PADDING_VALUE; - } - - // get the current uiNode - UiElementNode uiNode = getCurrentNode(); - - // get its xml node - Node xmlNode = uiNode.getXmlNode(); - - if (xmlNode != null) { - if (ATTR_LAYOUT.equals(localName) && VIEW_FRAGMENT.equals(xmlNode.getNodeName())) { - String layout = FragmentMenu.getFragmentLayout(xmlNode); - if (layout != null) { - return layout; - } - } - - Node attribute = xmlNode.getAttributes().getNamedItemNS(namespace, localName); - - // Auto-convert http://schemas.android.com/apk/res-auto resources. The lookup - // will be for the current application's resource package, e.g. - // http://schemas.android.com/apk/res/foo.bar, but the XML document will - // be using http://schemas.android.com/apk/res-auto in library projects: - if (attribute == null && namespace != null && !namespace.equals(ANDROID_URI)) { - attribute = xmlNode.getAttributes().getNamedItemNS(AUTO_URI, localName); - } - - if (attribute != null) { - String value = attribute.getNodeValue(); - if (mIncreaseExistingPadding && ATTR_PADDING.equals(localName) && - ANDROID_URI.equals(namespace)) { - // add the padding and return the value - return addPaddingToValue(value); - } - - // on the fly convert match_parent to fill_parent for compatibility with older - // platforms. - if (VALUE_MATCH_PARENT.equals(value) && - (ATTR_LAYOUT_WIDTH.equals(localName) || - ATTR_LAYOUT_HEIGHT.equals(localName)) && - ANDROID_URI.equals(namespace)) { - return VALUE_FILL_PARENT; - } - - // Handle unicode escapes etc - value = ValueXmlHelper.unescapeResourceString(value, false, false); - - return value; - } - } - - return null; - } - - @Override - public int getDepth() { - return mNodeStack.size(); - } - - @Override - public String getName() { - if (mParsingState == START_TAG || mParsingState == END_TAG) { - String name = getCurrentNode().getDescriptor().getXmlLocalName(); - - if (name.equals(VIEW_FRAGMENT)) { - // Temporarily translate <fragment> to <include> (and in getAttribute - // we will also provide a layout-attribute for the corresponding - // fragment name attribute) - String layout = FragmentMenu.getFragmentLayout(getCurrentNode().getXmlNode()); - if (layout != null) { - return VIEW_INCLUDE; - } - } - - return name; - } - - return null; - } - - @Override - public String getNamespace() { - if (mParsingState == START_TAG || mParsingState == END_TAG) { - return getCurrentNode().getDescriptor().getNamespace(); - } - - return null; - } - - @Override - public String getPrefix() { - if (mParsingState == START_TAG || mParsingState == END_TAG) { - Document doc = mRoot.getXmlDocument(); - return doc.lookupPrefix(getCurrentNode().getDescriptor().getNamespace()); - } - - return null; - } - - @Override - public boolean isEmptyElementTag() throws XmlPullParserException { - if (mParsingState == START_TAG) { - return getCurrentNode().getUiChildren().size() == 0; - } - - throw new XmlPullParserException("Call to isEmptyElementTag while not in START_TAG", - this, null); - } - - @Override - public void onNextFromStartDocument() { - onNextFromStartTag(); - } - - @Override - public void onNextFromStartTag() { - // get the current node, and look for text or children (children first) - UiElementNode node = getCurrentNode(); - List<UiElementNode> children = node.getUiChildren(); - if (children.size() > 0) { - // move to the new child, and don't change the state. - push(children.get(0)); - - // in case the current state is CURRENT_DOC, we set the proper state. - mParsingState = START_TAG; - } else { - if (mParsingState == START_DOCUMENT) { - // this handles the case where there's no node. - mParsingState = END_DOCUMENT; - } else { - mParsingState = END_TAG; - } - } - } - - @Override - public void onNextFromEndTag() { - // look for a sibling. if no sibling, go back to the parent - UiElementNode node = getCurrentNode(); - node = node.getUiNextSibling(); - if (node != null) { - // to go to the sibling, we need to remove the current node, - pop(); - // and add its sibling. - push(node); - mParsingState = START_TAG; - } else { - // move back to the parent - pop(); - - // we have only one element left (mRoot), then we're done with the document. - if (mNodeStack.size() == 1) { - mParsingState = END_DOCUMENT; - } else { - mParsingState = END_TAG; - } - } - } - - // ------- TypedValue stuff - // This is adapted from com.android.layoutlib.bridge.ResourceHelper - // (but modified to directly take the parsed value and convert it into pixel instead of - // storing it into a TypedValue) - // this was originally taken from platform/frameworks/base/libs/utils/ResourceTypes.cpp - - private static final class DimensionEntry { - String name; - int type; - - DimensionEntry(String name, int unit) { - this.name = name; - this.type = unit; - } - } - - /** {@link DimensionEntry} complex unit: Value is raw pixels. */ - private static final int COMPLEX_UNIT_PX = 0; - /** {@link DimensionEntry} complex unit: Value is Device Independent - * Pixels. */ - private static final int COMPLEX_UNIT_DIP = 1; - /** {@link DimensionEntry} complex unit: Value is a scaled pixel. */ - private static final int COMPLEX_UNIT_SP = 2; - /** {@link DimensionEntry} complex unit: Value is in points. */ - private static final int COMPLEX_UNIT_PT = 3; - /** {@link DimensionEntry} complex unit: Value is in inches. */ - private static final int COMPLEX_UNIT_IN = 4; - /** {@link DimensionEntry} complex unit: Value is in millimeters. */ - private static final int COMPLEX_UNIT_MM = 5; - - private final static DimensionEntry[] sDimensions = new DimensionEntry[] { - new DimensionEntry(UNIT_PX, COMPLEX_UNIT_PX), - new DimensionEntry(UNIT_DIP, COMPLEX_UNIT_DIP), - new DimensionEntry(UNIT_DP, COMPLEX_UNIT_DIP), - new DimensionEntry(UNIT_SP, COMPLEX_UNIT_SP), - new DimensionEntry(UNIT_PT, COMPLEX_UNIT_PT), - new DimensionEntry(UNIT_IN, COMPLEX_UNIT_IN), - new DimensionEntry(UNIT_MM, COMPLEX_UNIT_MM), - }; - - /** - * Adds padding to an existing dimension. - * <p/>This will resolve the attribute value (which can be px, dip, dp, sp, pt, in, mm) to - * a pixel value, add the padding value ({@link ExplodedRenderingHelper#PADDING_VALUE}), - * and then return a string with the new value as a px string ("42px"); - * If the conversion fails, only the special padding is returned. - */ - private String addPaddingToValue(String s) { - int padding = ExplodedRenderingHelper.PADDING_VALUE; - if (stringToPixel(s)) { - padding += sIntOut[0]; - } - - return padding + UNIT_PX; - } - - /** - * Convert the string into a pixel value, and puts it in {@link #sIntOut} - * @param s the dimension value from an XML attribute - * @return true if success. - */ - private boolean stringToPixel(String s) { - // remove the space before and after - s = s.trim(); - int len = s.length(); - - if (len <= 0) { - return false; - } - - // check that there's no non ASCII characters. - char[] buf = s.toCharArray(); - for (int i = 0 ; i < len ; i++) { - if (buf[i] > 255) { - return false; - } - } - - // check the first character - if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { - return false; - } - - // now look for the string that is after the float... - Matcher m = FLOAT_PATTERN.matcher(s); - if (m.matches()) { - String f_str = m.group(1); - String end = m.group(2); - - float f; - try { - f = Float.parseFloat(f_str); - } catch (NumberFormatException e) { - // this shouldn't happen with the regexp above. - return false; - } - - if (end.length() > 0 && end.charAt(0) != ' ') { - // We only support dimension-type values, so try to parse the unit for dimension - DimensionEntry dimension = parseDimension(end); - if (dimension != null) { - // convert the value into pixel based on the dimention type - // This is similar to TypedValue.applyDimension() - switch (dimension.type) { - case COMPLEX_UNIT_PX: - // do nothing, value is already in px - break; - case COMPLEX_UNIT_DIP: - case COMPLEX_UNIT_SP: // intended fall-through since we don't - // adjust for font size - f *= (float)mDensity.getDpiValue() / Density.DEFAULT_DENSITY; - break; - case COMPLEX_UNIT_PT: - f *= mDensity.getDpiValue() * (1.0f / 72); - break; - case COMPLEX_UNIT_IN: - f *= mDensity.getDpiValue(); - break; - case COMPLEX_UNIT_MM: - f *= mDensity.getDpiValue() * (1.0f / 25.4f); - break; - } - - // store result (converted to int) - sIntOut[0] = (int) (f + 0.5); - - return true; - } - } - } - - return false; - } - - private static DimensionEntry parseDimension(String str) { - str = str.trim(); - - for (DimensionEntry d : sDimensions) { - if (d.name.equals(str)) { - return d; - } - } - - return null; - } -} |