Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2011 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.util;
     18 
     19 import com.android.util.PositionXmlParser.Position;
     20 
     21 import org.w3c.dom.Attr;
     22 import org.w3c.dom.Document;
     23 import org.w3c.dom.Element;
     24 import org.w3c.dom.NodeList;
     25 import org.w3c.dom.Text;
     26 
     27 import java.io.BufferedOutputStream;
     28 import java.io.BufferedWriter;
     29 import java.io.File;
     30 import java.io.FileInputStream;
     31 import java.io.FileOutputStream;
     32 import java.io.FileWriter;
     33 import java.io.OutputStreamWriter;
     34 import java.io.Writer;
     35 
     36 import junit.framework.TestCase;
     37 
     38 @SuppressWarnings("javadoc")
     39 public class PositionXmlParserTest extends TestCase {
     40     public void test() throws Exception {
     41         String xml =
     42                 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
     43                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
     44                 "    android:layout_width=\"match_parent\"\n" +
     45                 "    android:layout_height=\"wrap_content\"\n" +
     46                 "    android:orientation=\"vertical\" >\n" +
     47                 "\n" +
     48                 "    <Button\n" +
     49                 "        android:id=\"@+id/button1\"\n" +
     50                 "        android:layout_width=\"wrap_content\"\n" +
     51                 "        android:layout_height=\"wrap_content\"\n" +
     52                 "        android:text=\"Button\" />\n" +
     53                 "\n" +
     54                 "    <Button\n" +
     55                 "        android:id=\"@+id/button2\"\n" +
     56                 "        android:layout_width=\"wrap_content\"\n" +
     57                 "        android:layout_height=\"wrap_content\"\n" +
     58                 "        android:text=\"Button\" />\n" +
     59                 "\n" +
     60                 "</LinearLayout>\n";
     61         PositionXmlParser parser = new PositionXmlParser();
     62         File file = File.createTempFile("parsertest", ".xml");
     63         Writer fw = new BufferedWriter(new FileWriter(file));
     64         fw.write(xml);
     65         fw.close();
     66         Document document = parser.parse(new FileInputStream(file));
     67         assertNotNull(document);
     68 
     69         // Basic parsing heart beat tests
     70         Element linearLayout = (Element) document.getElementsByTagName("LinearLayout").item(0);
     71         assertNotNull(linearLayout);
     72         NodeList buttons = document.getElementsByTagName("Button");
     73         assertEquals(2, buttons.getLength());
     74         final String ANDROID_URI = "http://schemas.android.com/apk/res/android";
     75         assertEquals("wrap_content",
     76                 linearLayout.getAttributeNS(ANDROID_URI, "layout_height"));
     77 
     78         // Check attribute positions
     79         Attr attr = linearLayout.getAttributeNodeNS(ANDROID_URI, "layout_width");
     80         assertNotNull(attr);
     81         Position start = parser.getPosition(attr);
     82         Position end = start.getEnd();
     83         assertEquals(2, start.getLine());
     84         assertEquals(4, start.getColumn());
     85         assertEquals(xml.indexOf("android:layout_width"), start.getOffset());
     86         assertEquals(2, end.getLine());
     87         String target = "android:layout_width=\"match_parent\"";
     88         assertEquals(xml.indexOf(target) + target.length(), end.getOffset());
     89 
     90         // Check element positions
     91         Element button = (Element) buttons.item(0);
     92         start = parser.getPosition(button);
     93         end = start.getEnd();
     94         assertNull(end.getEnd());
     95         assertEquals(6, start.getLine());
     96         assertEquals(4, start.getColumn());
     97         assertEquals(xml.indexOf("<Button"), start.getOffset());
     98         assertEquals(xml.indexOf("/>") + 2, end.getOffset());
     99         assertEquals(10, end.getLine());
    100         int button1End = end.getOffset();
    101 
    102         Element button2 = (Element) buttons.item(1);
    103         start = parser.getPosition(button2);
    104         end = start.getEnd();
    105         assertEquals(12, start.getLine());
    106         assertEquals(4, start.getColumn());
    107         assertEquals(xml.indexOf("<Button", button1End), start.getOffset());
    108         assertEquals(xml.indexOf("/>", start.getOffset()) + 2, end.getOffset());
    109         assertEquals(16, end.getLine());
    110 
    111         file.delete();
    112     }
    113 
    114     public void testText() throws Exception {
    115         String xml =
    116                 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
    117                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
    118                 "    android:layout_width=\"match_parent\"\n" +
    119                 "    android:layout_height=\"wrap_content\"\n" +
    120                 "    android:orientation=\"vertical\" >\n" +
    121                 "\n" +
    122                 "    <Button\n" +
    123                 "        android:id=\"@+id/button1\"\n" +
    124                 "        android:layout_width=\"wrap_content\"\n" +
    125                 "        android:layout_height=\"wrap_content\"\n" +
    126                 "        android:text=\"Button\" />\n" +
    127                 "          some text\n" +
    128                 "\n" +
    129                 "</LinearLayout>\n";
    130         PositionXmlParser parser = new PositionXmlParser();
    131         File file = File.createTempFile("parsertest", ".xml");
    132         file.deleteOnExit();
    133         Writer fw = new BufferedWriter(new FileWriter(file));
    134         fw.write(xml);
    135         fw.close();
    136         Document document = parser.parse(new FileInputStream(file));
    137         assertNotNull(document);
    138 
    139         // Basic parsing heart beat tests
    140         Element linearLayout = (Element) document.getElementsByTagName("LinearLayout").item(0);
    141         assertNotNull(linearLayout);
    142         NodeList buttons = document.getElementsByTagName("Button");
    143         assertEquals(1, buttons.getLength());
    144         final String ANDROID_URI = "http://schemas.android.com/apk/res/android";
    145         assertEquals("wrap_content",
    146                 linearLayout.getAttributeNS(ANDROID_URI, "layout_height"));
    147 
    148         // Check text positions
    149         Element button = (Element) buttons.item(0);
    150         Text text = (Text) button.getNextSibling();
    151         assertNotNull(text);
    152 
    153         // Check attribute positions
    154         Position start = parser.getPosition(text);
    155         assertEquals(11, start.getLine());
    156         assertEquals(10, start.getColumn());
    157         assertEquals(xml.indexOf("some text"), start.getOffset());
    158     }
    159 
    160     public void testLineEndings() throws Exception {
    161         // Test for http://code.google.com/p/android/issues/detail?id=22925
    162         String xml =
    163                 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" +
    164                 "<LinearLayout>\r\n" +
    165                 "\r" +
    166                 "<LinearLayout></LinearLayout>\r\n" +
    167                 "</LinearLayout>\r\n";
    168         PositionXmlParser parser = new PositionXmlParser();
    169         File file = File.createTempFile("parsertest2", ".xml");
    170         Writer fw = new BufferedWriter(new FileWriter(file));
    171         fw.write(xml);
    172         fw.close();
    173         Document document = parser.parse(new FileInputStream(file));
    174         assertNotNull(document);
    175 
    176         file.delete();
    177     }
    178 
    179     private static void checkEncoding(String encoding, boolean writeBom, boolean writeEncoding,
    180             String lineEnding)
    181             throws Exception {
    182         // Norwegian extra vowel characters such as "latin small letter a with ring above"
    183         String value = "\u00e6\u00d8\u00e5";
    184         StringBuilder sb = new StringBuilder();
    185 
    186         sb.append("<?xml version=\"1.0\"");
    187         if (writeEncoding) {
    188             sb.append(" encoding=\"");
    189             sb.append(encoding);
    190             sb.append("\"");
    191         }
    192         sb.append("?>");
    193         sb.append(lineEnding);
    194         sb.append(
    195                 "<!-- This is a " + lineEnding +
    196                 "     multiline comment" + lineEnding +
    197                 "-->" + lineEnding +
    198                 "<foo ");
    199         int startAttrOffset = sb.length();
    200         sb.append("attr=\"");
    201         sb.append(value);
    202         sb.append("\"");
    203         sb.append(">" + lineEnding +
    204                 lineEnding +
    205                 "<bar></bar>" + lineEnding +
    206                 "</foo>" + lineEnding);
    207         PositionXmlParser parser = new PositionXmlParser();
    208         File file = File.createTempFile("parsertest" + encoding + writeBom + writeEncoding,
    209                 ".xml");
    210         BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
    211         OutputStreamWriter writer = new OutputStreamWriter(stream, encoding);
    212 
    213         if (writeBom) {
    214             String normalized = encoding.toLowerCase().replace("-", "_");
    215             if (normalized.equals("utf_8")) {
    216                 stream.write(0xef);
    217                 stream.write(0xbb);
    218                 stream.write(0xbf);
    219             } else if (normalized.equals("utf_16")) {
    220                 stream.write(0xfe);
    221                 stream.write(0xff);
    222             } else if (normalized.equals("utf_16le")) {
    223                 stream.write(0xff);
    224                 stream.write(0xfe);
    225             } else if (normalized.equals("utf_32")) {
    226                 stream.write(0x0);
    227                 stream.write(0x0);
    228                 stream.write(0xfe);
    229                 stream.write(0xff);
    230             } else if (normalized.equals("utf_32le")) {
    231                 stream.write(0xff);
    232                 stream.write(0xfe);
    233                 stream.write(0x0);
    234                 stream.write(0x0);
    235             } else {
    236                 fail("Can't write BOM for encoding " + encoding);
    237             }
    238         }
    239 
    240         writer.write(sb.toString());
    241         writer.close();
    242 
    243         Document document = parser.parse(new FileInputStream(file));
    244         assertNotNull(document);
    245         Element root = document.getDocumentElement();
    246         assertEquals(file.getPath(), value, root.getAttribute("attr"));
    247         assertEquals(4, parser.getPosition(root).getLine());
    248 
    249         Attr attribute = root.getAttributeNode("attr");
    250         assertNotNull(attribute);
    251         Position position = parser.getPosition(attribute);
    252         assertNotNull(position);
    253         assertEquals(4, position.getLine());
    254         assertEquals(startAttrOffset, position.getOffset());
    255 
    256         file.delete();
    257     }
    258 
    259     public void testEncoding() throws Exception {
    260         checkEncoding("utf-8", false /*bom*/, true /*encoding*/, "\n");
    261         checkEncoding("UTF-8", false /*bom*/, true /*encoding*/, "\n");
    262         checkEncoding("UTF_16", false /*bom*/, true /*encoding*/, "\n");
    263         checkEncoding("UTF-16", false /*bom*/, true /*encoding*/, "\n");
    264         checkEncoding("UTF_16LE", false /*bom*/, true /*encoding*/, "\n");
    265         checkEncoding("UTF_32", false /*bom*/, true /*encoding*/, "\n");
    266         checkEncoding("UTF_32LE", false /*bom*/, true /*encoding*/, "\n");
    267         checkEncoding("windows-1252", false /*bom*/, true /*encoding*/, "\n");
    268         checkEncoding("MacRoman", false /*bom*/, true /*encoding*/, "\n");
    269         checkEncoding("ISO-8859-1", false /*bom*/, true /*encoding*/, "\n");
    270         checkEncoding("iso-8859-1", false /*bom*/, true /*encoding*/, "\n");
    271 
    272         // Try BOM's (with no encoding specified)
    273         checkEncoding("utf-8", true /*bom*/, false /*encoding*/, "\n");
    274         checkEncoding("UTF-8", true /*bom*/, false /*encoding*/, "\n");
    275         checkEncoding("UTF_16", true /*bom*/, false /*encoding*/, "\n");
    276         checkEncoding("UTF-16", true /*bom*/, false /*encoding*/, "\n");
    277         checkEncoding("UTF_16LE", true /*bom*/, false /*encoding*/, "\n");
    278         checkEncoding("UTF_32", true /*bom*/, false /*encoding*/, "\n");
    279         checkEncoding("UTF_32LE", true /*bom*/, false /*encoding*/, "\n");
    280 
    281         // Try default encodings (only defined for utf-8 and utf-16)
    282         checkEncoding("utf-8", false /*bom*/, false /*encoding*/, "\n");
    283         checkEncoding("UTF-8", false /*bom*/, false /*encoding*/, "\n");
    284         checkEncoding("UTF_16", false /*bom*/, false /*encoding*/, "\n");
    285         checkEncoding("UTF-16", false /*bom*/, false /*encoding*/, "\n");
    286         checkEncoding("UTF_16LE", false /*bom*/, false /*encoding*/, "\n");
    287 
    288         // Try BOM's (with explicit encoding specified)
    289         checkEncoding("utf-8", true /*bom*/, true /*encoding*/, "\n");
    290         checkEncoding("UTF-8", true /*bom*/, true /*encoding*/, "\n");
    291         checkEncoding("UTF_16", true /*bom*/, true /*encoding*/, "\n");
    292         checkEncoding("UTF-16", true /*bom*/, true /*encoding*/, "\n");
    293         checkEncoding("UTF_16LE", true /*bom*/, true /*encoding*/, "\n");
    294         checkEncoding("UTF_32", true /*bom*/, true /*encoding*/, "\n");
    295         checkEncoding("UTF_32LE", true /*bom*/, true /*encoding*/, "\n");
    296 
    297         // Make sure this works for \r and \r\n as well
    298         checkEncoding("UTF-16", false /*bom*/, true /*encoding*/, "\r");
    299         checkEncoding("UTF_16LE", false /*bom*/, true /*encoding*/, "\r");
    300         checkEncoding("UTF_32", false /*bom*/, true /*encoding*/, "\r");
    301         checkEncoding("UTF_32LE", false /*bom*/, true /*encoding*/, "\r");
    302         checkEncoding("windows-1252", false /*bom*/, true /*encoding*/, "\r");
    303         checkEncoding("MacRoman", false /*bom*/, true /*encoding*/, "\r");
    304         checkEncoding("ISO-8859-1", false /*bom*/, true /*encoding*/, "\r");
    305         checkEncoding("iso-8859-1", false /*bom*/, true /*encoding*/, "\r");
    306 
    307         checkEncoding("UTF-16", false /*bom*/, true /*encoding*/, "\r\n");
    308         checkEncoding("UTF_16LE", false /*bom*/, true /*encoding*/, "\r\n");
    309         checkEncoding("UTF_32", false /*bom*/, true /*encoding*/, "\r\n");
    310         checkEncoding("UTF_32LE", false /*bom*/, true /*encoding*/, "\r\n");
    311         checkEncoding("windows-1252", false /*bom*/, true /*encoding*/, "\r\n");
    312         checkEncoding("MacRoman", false /*bom*/, true /*encoding*/, "\r\n");
    313         checkEncoding("ISO-8859-1", false /*bom*/, true /*encoding*/, "\r\n");
    314         checkEncoding("iso-8859-1", false /*bom*/, true /*encoding*/, "\r\n");
    315     }
    316 }
    317