2 * Copyright (C) 2009 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.layoutopt.uix.groovy;
19 import com.android.layoutopt.uix.LayoutAnalysis;
20 import com.android.layoutopt.uix.xml.XmlDocumentBuilder;
23 import java.util.List;
24 import java.util.ArrayList;
25 import java.util.Arrays;
27 import groovy.lang.GString;
28 import groovy.xml.dom.DOMCategory;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
31 import org.w3c.dom.Element;
34 * Support class for Groovy rules. This class adds new Groovy capabilities
35 * to {@link com.android.layoutopt.uix.LayoutAnalysis} and {@link org.w3c.dom.Node}.
37 public class LayoutAnalysisCategory {
38 private static final String ANDROID_PADDING = "android:padding";
39 private static final String ANDROID_PADDING_LEFT = "android:paddingLeft";
40 private static final String ANDROID_PADDING_TOP = "android:paddingTop";
41 private static final String ANDROID_PADDING_RIGHT = "android:paddingRight";
42 private static final String ANDROID_PADDING_BOTTOM = "android:paddingBottom";
43 private static final String ANDROID_LAYOUT_WIDTH = "android:layout_width";
44 private static final String ANDROID_LAYOUT_HEIGHT = "android:layout_height";
45 private static final String VALUE_FILL_PARENT = "match_parent";
46 private static final String VALUE_WRAP_CONTENT = "wrap_content";
48 private static final String[] sContainers = new String[] {
49 "FrameLayout", "LinearLayout", "RelativeLayout", "SlidingDrawer",
50 "AbsoluteLayout", "TableLayout", "Gallery", "GridView", "ListView",
51 "RadioGroup", "ScrollView", "HorizontalScrollView", "Spinner",
52 "ViewSwitcher", "ViewFlipper", "ViewAnimator", "ImageSwitcher",
53 "TextSwitcher", "android.gesture.GestureOverlayView", "TabHost"
56 Arrays.sort(sContainers);
60 * xmlNode.isContainer()
62 * @return True if the specified node corresponds to a container widget.
64 public static boolean isContainer(Element element) {
65 return Arrays.binarySearch(sContainers, element.getNodeName()) >= 0;
71 * Same as xmlNode.'**' but excludes xmlNode from the results.
73 * @return All descendants, this node excluded.
75 public static List<Node> all(Element element) {
76 NodeList list = DOMCategory.depthFirst(element);
77 int count = list.getLength();
78 List<Node> nodes = new ArrayList<Node>(count - 1);
79 for (int i = 1; i < count; i++) {
80 nodes.add(list.item(i));
86 * Returns the start line of this node.
88 * @return The start line or -1 if the line is unknown.
90 public static int getStartLine(Node node) {
91 final Object data = node == null ? null :
92 node.getUserData(XmlDocumentBuilder.NODE_START_LINE);
93 return data == null ? -1 : (Integer) data;
97 * Returns the end line of this node.
99 * @return The end line or -1 if the line is unknown.
101 public static int getEndLine(Node node) {
102 final Object data = node == null ? null :
103 node.getUserData(XmlDocumentBuilder.NODE_END_LINE);
104 return data == null ? -1 : (Integer) data;
108 * xmlNode.hasPadding()
110 * @return True if the node has one ore more padding attributes.
112 public static boolean hasPadding(Element element) {
113 return element.getAttribute(ANDROID_PADDING).length() > 0 ||
114 element.getAttribute(ANDROID_PADDING_LEFT).length() > 0 ||
115 element.getAttribute(ANDROID_PADDING_TOP).length() > 0 ||
116 element.getAttribute(ANDROID_PADDING_BOTTOM).length() > 0 ||
117 element.getAttribute(ANDROID_PADDING_RIGHT).length() > 0;
121 * Returns whether this node's width is match_parent.
123 public static boolean isWidthFillParent(Element element) {
124 return element.getAttribute(ANDROID_LAYOUT_WIDTH).equals(VALUE_FILL_PARENT);
128 * Returns whether this node's width is wrap_content.
130 public static boolean isWidthWrapContent(Element element) {
131 return element.getAttribute(ANDROID_LAYOUT_WIDTH).equals(VALUE_WRAP_CONTENT);
135 * Returns whether this node's height is match_parent.
137 public static boolean isHeightFillParent(Element element) {
138 return element.getAttribute(ANDROID_LAYOUT_HEIGHT).equals(VALUE_FILL_PARENT);
142 * Returns whether this node's height is wrap_content.
144 public static boolean isHeightWrapContent(Element element) {
145 return element.getAttribute(ANDROID_LAYOUT_HEIGHT).equals(VALUE_WRAP_CONTENT);
151 * @return True if xmlNode is the root of the document, false otherwise
153 public static boolean isRoot(Node node) {
154 return node.getOwnerDocument().getDocumentElement() == node;
158 * xmlNode.is("tagName")
160 * @return True if xmlNode.getNodeName().equals(name), false otherwise.
162 public static boolean is(Node node, String name) {
163 return node.getNodeName().equals(name);
169 * @return The maximum depth of the node.
171 public static int depth(Node node) {
173 NodeList list = node.getChildNodes();
174 int count = list.getLength();
176 for (int i = 0; i < count; i++) {
177 maxDepth = Math.max(maxDepth, depth(list.item(i)));
184 * analysis << "The issue"
186 * @return The analysis itself to chain calls.
188 public static LayoutAnalysis leftShift(LayoutAnalysis analysis, GString description) {
189 analysis.addIssue(description.toString());
194 * analysis << "The issue"
196 * @return The analysis itself to chain calls.
198 public static LayoutAnalysis leftShift(LayoutAnalysis analysis, String description) {
199 analysis.addIssue(description);
204 * analysis << [node: node, description: "The issue"]
206 * @return The analysis itself to chain calls.
208 public static LayoutAnalysis leftShift(LayoutAnalysis analysis, Map issue) {
209 analysis.addIssue((Node) issue.get("node"), issue.get("description").toString());