• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package com.android.layoutopt.uix.groovy;
18 
19 import com.android.layoutopt.uix.LayoutAnalysis;
20 import com.android.layoutopt.uix.xml.XmlDocumentBuilder;
21 
22 import java.util.Map;
23 import java.util.List;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 
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;
32 
33 /**
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}.
36  */
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 = "fill_parent";
46     private static final String VALUE_MATCH_PARENT = "match_parent";
47     private static final String VALUE_WRAP_CONTENT = "wrap_content";
48 
49     private static final String[] sContainers = new String[] {
50             "FrameLayout", "LinearLayout", "RelativeLayout", "SlidingDrawer",
51             "AbsoluteLayout", "TableLayout", "Gallery", "GridView", "ListView",
52             "RadioGroup", "ScrollView", "HorizontalScrollView", "Spinner",
53             "ViewSwitcher", "ViewFlipper", "ViewAnimator", "ImageSwitcher",
54             "TextSwitcher", "android.gesture.GestureOverlayView", "TabHost"
55     };
56     static {
57         Arrays.sort(sContainers);
58     }
59 
60     /**
61      * xmlNode.isContainer()
62      *
63      * @return True if the specified node corresponds to a container widget.
64      */
isContainer(Element element)65     public static boolean isContainer(Element element) {
66         return Arrays.binarySearch(sContainers, element.getNodeName()) >= 0;
67     }
68 
69     /**
70      * xmlNode.all()
71      *
72      * Same as xmlNode.'**' but excludes xmlNode from the results.
73      *
74      * @return All descendants, this node excluded.
75      */
all(Element element)76     public static List<Node> all(Element element) {
77         NodeList list = DOMCategory.depthFirst(element);
78         int count = list.getLength();
79         List<Node> nodes = new ArrayList<Node>(count - 1);
80         for (int i = 1; i < count; i++) {
81             nodes.add(list.item(i));
82         }
83         return nodes;
84     }
85 
86     /**
87      * Returns the start line of this node.
88      *
89      * @return The start line or -1 if the line is unknown.
90      */
getStartLine(Node node)91     public static int getStartLine(Node node) {
92         final Object data = node == null ? null :
93                 node.getUserData(XmlDocumentBuilder.NODE_START_LINE);
94         return data == null ? -1 : (Integer) data;
95     }
96 
97     /**
98      * Returns the end line of this node.
99      *
100      * @return The end line or -1 if the line is unknown.
101      */
getEndLine(Node node)102     public static int getEndLine(Node node) {
103         final Object data = node == null ? null :
104                 node.getUserData(XmlDocumentBuilder.NODE_END_LINE);
105         return data == null ? -1 : (Integer) data;
106     }
107 
108     /**
109      * xmlNode.hasPadding()
110      *
111      * @return True if the node has one ore more padding attributes.
112      */
hasPadding(Element element)113     public static boolean hasPadding(Element element) {
114         return element.getAttribute(ANDROID_PADDING).length() > 0 ||
115                 element.getAttribute(ANDROID_PADDING_LEFT).length() > 0 ||
116                 element.getAttribute(ANDROID_PADDING_TOP).length() > 0 ||
117                 element.getAttribute(ANDROID_PADDING_BOTTOM).length() > 0 ||
118                 element.getAttribute(ANDROID_PADDING_RIGHT).length() > 0;
119     }
120 
121     /**
122      * Returns whether this node's width is match_parent.
123      */
isWidthFillParent(Element element)124     public static boolean isWidthFillParent(Element element) {
125         final String attribute = element.getAttribute(ANDROID_LAYOUT_WIDTH);
126         return attribute.equals(VALUE_FILL_PARENT) || attribute.equals(VALUE_MATCH_PARENT);
127     }
128 
129     /**
130      * Returns whether this node's width is wrap_content.
131      */
isWidthWrapContent(Element element)132     public static boolean isWidthWrapContent(Element element) {
133         return element.getAttribute(ANDROID_LAYOUT_WIDTH).equals(VALUE_WRAP_CONTENT);
134     }
135 
136     /**
137      * Returns whether this node's height is match_parent.
138      */
isHeightFillParent(Element element)139     public static boolean isHeightFillParent(Element element) {
140         final String attribute = element.getAttribute(ANDROID_LAYOUT_HEIGHT);
141         return attribute.equals(VALUE_FILL_PARENT) || attribute.equals(VALUE_MATCH_PARENT);
142     }
143 
144     /**
145      * Returns whether this node's height is wrap_content.
146      */
isHeightWrapContent(Element element)147     public static boolean isHeightWrapContent(Element element) {
148         return element.getAttribute(ANDROID_LAYOUT_HEIGHT).equals(VALUE_WRAP_CONTENT);
149     }
150 
151     /**
152      * xmlNode.isRoot()
153      *
154      * @return True if xmlNode is the root of the document, false otherwise
155      */
isRoot(Node node)156     public static boolean isRoot(Node node) {
157         return node.getOwnerDocument().getDocumentElement() == node;
158     }
159 
160     /**
161      * xmlNode.is("tagName")
162      *
163      * @return True if xmlNode.getNodeName().equals(name), false otherwise.
164      */
is(Node node, String name)165     public static boolean is(Node node, String name) {
166         return node.getNodeName().equals(name);
167     }
168 
169     /**
170      * xmlNode.depth()
171      *
172      * @return The maximum depth of the node.
173      */
depth(Node node)174     public static int depth(Node node) {
175         int maxDepth = 0;
176         NodeList list = node.getChildNodes();
177         int count = list.getLength();
178 
179         for (int i = 0; i < count; i++) {
180             maxDepth = Math.max(maxDepth, depth(list.item(i)));
181         }
182 
183         return maxDepth + 1;
184     }
185 
186     /**
187      * analysis << "The issue"
188      *
189      * @return The analysis itself to chain calls.
190      */
leftShift(LayoutAnalysis analysis, GString description)191     public static LayoutAnalysis leftShift(LayoutAnalysis analysis, GString description) {
192         analysis.addIssue(description.toString());
193         return analysis;
194     }
195 
196     /**
197      * analysis << "The issue"
198      *
199      * @return The analysis itself to chain calls.
200      */
leftShift(LayoutAnalysis analysis, String description)201     public static LayoutAnalysis leftShift(LayoutAnalysis analysis, String description) {
202         analysis.addIssue(description);
203         return analysis;
204     }
205 
206     /**
207      * analysis << [node: node, description: "The issue"]
208      *
209      * @return The analysis itself to chain calls.
210      */
leftShift(LayoutAnalysis analysis, Map issue)211     public static LayoutAnalysis leftShift(LayoutAnalysis analysis, Map issue) {
212         analysis.addIssue((Node) issue.get("node"), issue.get("description").toString());
213         return analysis;
214     }
215 }
216