Home | History | Annotate | Download | only in groovy
      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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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