/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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.layoutopt.uix.groovy; import com.android.layoutopt.uix.LayoutAnalysis; import com.android.layoutopt.uix.xml.XmlDocumentBuilder; import java.util.Map; import java.util.List; import java.util.ArrayList; import java.util.Arrays; import groovy.lang.GString; import groovy.xml.dom.DOMCategory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Element; /** * Support class for Groovy rules. This class adds new Groovy capabilities * to {@link com.android.layoutopt.uix.LayoutAnalysis} and {@link org.w3c.dom.Node}. */ public class LayoutAnalysisCategory { private static final String ANDROID_PADDING = "android:padding"; private static final String ANDROID_PADDING_LEFT = "android:paddingLeft"; private static final String ANDROID_PADDING_TOP = "android:paddingTop"; private static final String ANDROID_PADDING_RIGHT = "android:paddingRight"; private static final String ANDROID_PADDING_BOTTOM = "android:paddingBottom"; private static final String ANDROID_LAYOUT_WIDTH = "android:layout_width"; private static final String ANDROID_LAYOUT_HEIGHT = "android:layout_height"; private static final String VALUE_FILL_PARENT = "fill_parent"; private static final String VALUE_MATCH_PARENT = "match_parent"; private static final String VALUE_WRAP_CONTENT = "wrap_content"; private static final String[] sContainers = new String[] { "FrameLayout", "LinearLayout", "RelativeLayout", "SlidingDrawer", "AbsoluteLayout", "TableLayout", "Gallery", "GridView", "ListView", "RadioGroup", "ScrollView", "HorizontalScrollView", "Spinner", "ViewSwitcher", "ViewFlipper", "ViewAnimator", "ImageSwitcher", "TextSwitcher", "android.gesture.GestureOverlayView", "TabHost" }; static { Arrays.sort(sContainers); } /** * xmlNode.isContainer() * * @return True if the specified node corresponds to a container widget. */ public static boolean isContainer(Element element) { return Arrays.binarySearch(sContainers, element.getNodeName()) >= 0; } /** * xmlNode.all() * * Same as xmlNode.'**' but excludes xmlNode from the results. * * @return All descendants, this node excluded. */ public static List<Node> all(Element element) { NodeList list = DOMCategory.depthFirst(element); int count = list.getLength(); List<Node> nodes = new ArrayList<Node>(count - 1); for (int i = 1; i < count; i++) { nodes.add(list.item(i)); } return nodes; } /** * Returns the start line of this node. * * @return The start line or -1 if the line is unknown. */ public static int getStartLine(Node node) { final Object data = node == null ? null : node.getUserData(XmlDocumentBuilder.NODE_START_LINE); return data == null ? -1 : (Integer) data; } /** * Returns the end line of this node. * * @return The end line or -1 if the line is unknown. */ public static int getEndLine(Node node) { final Object data = node == null ? null : node.getUserData(XmlDocumentBuilder.NODE_END_LINE); return data == null ? -1 : (Integer) data; } /** * xmlNode.hasPadding() * * @return True if the node has one ore more padding attributes. */ public static boolean hasPadding(Element element) { return element.getAttribute(ANDROID_PADDING).length() > 0 || element.getAttribute(ANDROID_PADDING_LEFT).length() > 0 || element.getAttribute(ANDROID_PADDING_TOP).length() > 0 || element.getAttribute(ANDROID_PADDING_BOTTOM).length() > 0 || element.getAttribute(ANDROID_PADDING_RIGHT).length() > 0; } /** * Returns whether this node's width is match_parent. */ public static boolean isWidthFillParent(Element element) { final String attribute = element.getAttribute(ANDROID_LAYOUT_WIDTH); return attribute.equals(VALUE_FILL_PARENT) || attribute.equals(VALUE_MATCH_PARENT); } /** * Returns whether this node's width is wrap_content. */ public static boolean isWidthWrapContent(Element element) { return element.getAttribute(ANDROID_LAYOUT_WIDTH).equals(VALUE_WRAP_CONTENT); } /** * Returns whether this node's height is match_parent. */ public static boolean isHeightFillParent(Element element) { final String attribute = element.getAttribute(ANDROID_LAYOUT_HEIGHT); return attribute.equals(VALUE_FILL_PARENT) || attribute.equals(VALUE_MATCH_PARENT); } /** * Returns whether this node's height is wrap_content. */ public static boolean isHeightWrapContent(Element element) { return element.getAttribute(ANDROID_LAYOUT_HEIGHT).equals(VALUE_WRAP_CONTENT); } /** * xmlNode.isRoot() * * @return True if xmlNode is the root of the document, false otherwise */ public static boolean isRoot(Node node) { return node.getOwnerDocument().getDocumentElement() == node; } /** * xmlNode.is("tagName") * * @return True if xmlNode.getNodeName().equals(name), false otherwise. */ public static boolean is(Node node, String name) { return node.getNodeName().equals(name); } /** * xmlNode.depth() * * @return The maximum depth of the node. */ public static int depth(Node node) { int maxDepth = 0; NodeList list = node.getChildNodes(); int count = list.getLength(); for (int i = 0; i < count; i++) { maxDepth = Math.max(maxDepth, depth(list.item(i))); } return maxDepth + 1; } /** * analysis << "The issue" * * @return The analysis itself to chain calls. */ public static LayoutAnalysis leftShift(LayoutAnalysis analysis, GString description) { analysis.addIssue(description.toString()); return analysis; } /** * analysis << "The issue" * * @return The analysis itself to chain calls. */ public static LayoutAnalysis leftShift(LayoutAnalysis analysis, String description) { analysis.addIssue(description); return analysis; } /** * analysis << [node: node, description: "The issue"] * * @return The analysis itself to chain calls. */ public static LayoutAnalysis leftShift(LayoutAnalysis analysis, Map issue) { analysis.addIssue((Node) issue.get("node"), issue.get("description").toString()); return analysis; } }