140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye/*
240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye * Copyright (C) 2011 The Android Open Source Project
340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye *
440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye * Licensed under the Eclipse Public License, Version 1.0 (the "License");
540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye * you may not use this file except in compliance with the License.
640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye * You may obtain a copy of the License at
740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye *
840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye *      http://www.eclipse.org/org/documents/epl-v10.php
940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye *
1040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye * Unless required by applicable law or agreed to in writing, software
1140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye * distributed under the License is distributed on an "AS IS" BASIS,
1240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye * See the License for the specific language governing permissions and
1440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye * limitations under the License.
1540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye */
1640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyepackage com.android.ide.eclipse.adt.internal.editors.layout.gle2;
1740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
1812d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_ID;
1912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ID_PREFIX;
2012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.NEW_ID_PREFIX;
2112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.TOOLS_PREFIX;
2212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.TOOLS_URI;
2340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
2440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport com.android.ide.common.layout.BaseLayoutRule;
2540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport com.android.ide.eclipse.adt.AdtPlugin;
26401e50139efc9333a689c2306ca66a7b2c840c40Tor Norbyeimport com.android.ide.eclipse.adt.AdtUtils;
2740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
2840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
2940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
3085e4a1a9dd133abb879ec211ce8dd385004edf22Xavier Ducrohetimport com.android.utils.Pair;
3185e4a1a9dd133abb879ec211ce8dd385004edf22Xavier Ducrohetimport com.android.utils.XmlUtils;
3240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
3340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport org.eclipse.core.resources.IFile;
3440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport org.eclipse.jface.text.IDocument;
3540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport org.eclipse.ui.IEditorPart;
3640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport org.eclipse.ui.PartInitException;
3740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
3840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
396c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbyeimport org.w3c.dom.Element;
4040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyeimport org.w3c.dom.Node;
4140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
426c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye/**
436c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye *
446c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye */
456c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye@SuppressWarnings({"restriction", "javadoc", "deprecation"}) // XML DOM model
4640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbyepublic class LayoutMetadataTest extends AdtProjectTest {
476c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye
486c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye    public void testOldMetadata1() throws Exception {
4940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        Pair<IDocument, UiElementNode> pair = getNode("metadata.xml", "listView1");
5040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        IDocument document = pair.getFirst();
5140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        UiElementNode uiNode = pair.getSecond();
5240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        Node node = uiNode.getXmlNode();
5340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
546c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        assertNull(LayoutMetadata.getProperty(document, node, "foo"));
5540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        String before =
5640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
5740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        android:layout_height=\"wrap_content\">\n" +
5840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "    </ListView>";
5940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertEquals(before, getText(document, node));
6040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
6140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        // Set the property
626c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        LayoutMetadata.setProperty(document, node,
6340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye                "listitem", "@android:layout/simple_list_item_checked");
6440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        String after =
6540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
6640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        android:layout_height=\"wrap_content\">\n" +
6740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        <!-- Preview: listitem=@android:layout/simple_list_item_checked -->\n" +
6840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "    </ListView>";
6940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertEquals(after, getText(document, node));
7040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
7140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        // Set a second property
726c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        LayoutMetadata.setProperty(document, node,
7340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye                "listheader", "@android:layout/browser_link_context_header");
7440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        after =
7540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
7640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        android:layout_height=\"wrap_content\">\n" +
7740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        <!-- Preview: \n" +
7840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "            listheader=@android:layout/browser_link_context_header\n" +
7940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "            listitem=@android:layout/simple_list_item_checked\n" +
8040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "         -->\n" +
8140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "    </ListView>";
8240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertEquals(after, getText(document, node));
8340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
8440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        // Set list item to a different layout
856c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        LayoutMetadata.setProperty(document, node,
8640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye                "listitem", "@android:layout/simple_list_item_single_choice");
8740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        after =
8840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
8940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        android:layout_height=\"wrap_content\">\n" +
9040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        <!-- Preview: \n" +
9140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "            listheader=@android:layout/browser_link_context_header\n" +
9240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "            listitem=@android:layout/simple_list_item_single_choice\n" +
9340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "         -->\n" +
9440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "    </ListView>";
9540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertEquals(after, getText(document, node));
9640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
9740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        // Set header to a different layout
986c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        LayoutMetadata.setProperty(document, node,
9940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye                "listheader", "@layout/foo");
10040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        after =
10140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
10240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        android:layout_height=\"wrap_content\">\n" +
10340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        <!-- Preview: \n" +
10440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "            listheader=@layout/foo\n" +
10540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "            listitem=@android:layout/simple_list_item_single_choice\n" +
10640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "         -->\n" +
10740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "    </ListView>";
10840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertEquals(after, getText(document, node));
10940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
11040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        // Clear out list item
1116c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        LayoutMetadata.setProperty(document, node,
11240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye                "listitem", null);
11340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        after =
11440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
11540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        android:layout_height=\"wrap_content\">\n" +
11640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        <!-- Preview: listheader=@layout/foo -->\n" +
11740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "    </ListView>";
11840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertEquals(after, getText(document, node));
11940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
12040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        // Clear out list header
1216c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        LayoutMetadata.setProperty(document, node,
12240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye                "listheader", null);
12340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        after =
12440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "<ListView android:layout_width=\"match_parent\" android:id=\"@+id/listView1\"\n" +
12540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        android:layout_height=\"wrap_content\"></ListView>";
12640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertEquals(after, getText(document, node));
12740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
12840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        // Check node expansion on the button which doesn't have an end tag:
12940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        before = "<Button android:text=\"Button\" android:id=\"@+id/button1\"/>";
13040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    }
13140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
13240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    public void testMetadata2() throws Exception {
13340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        Pair<IDocument, UiElementNode> pair = getNode("metadata.xml", "button1");
13440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        IDocument document = pair.getFirst();
13540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        UiElementNode uiNode = pair.getSecond();
13640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        Node node = uiNode.getXmlNode();
13740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
1386c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        assertNull(LayoutMetadata.getProperty(document, node, "foo"));
13940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        String before =
14040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "<Button android:text=\"Button\" android:id=\"@+id/button1\"/>";
14140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertEquals(before, getText(document, node));
14240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
14340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        // Set the property
1446c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        LayoutMetadata.setProperty(document, node,
14540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye                "listitem", "@android:layout/simple_list_item_checked");
14640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        String after =
14740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "<Button android:text=\"Button\" android:id=\"@+id/button1\">\n" +
14840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "        <!-- Preview: listitem=@android:layout/simple_list_item_checked -->\n" +
14940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            "    </Button>";
15040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertEquals(after, getText(document, node));
15140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    }
15240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
1536c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye    public void testMetadata1() throws Exception {
1546c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        Pair<IDocument, UiElementNode> pair = getNode("metadata.xml", "listView1");
1556c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        UiElementNode uiNode = pair.getSecond();
1566c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        Node node = uiNode.getXmlNode();
1576c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye
1586c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        assertNull(LayoutMetadata.getProperty(node, "foo"));
1596c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye
1606c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        Element element = (Element) node;
1616c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        String prefix = XmlUtils.lookupNamespacePrefix(element, TOOLS_URI, null);
1626c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        if (prefix == null) {
1636c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye            // Add in new prefix...
1646c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye            prefix = XmlUtils.lookupNamespacePrefix(element,
1656c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye                    TOOLS_URI, TOOLS_PREFIX);
1666c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        }
1676c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye        element.setAttribute(prefix + ':' + "foo", "bar");
1686c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye    }
1696c75fe8af23e154a5cd90a7272fa9f6aa8648e51Tor Norbye
17040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    // ==== Test utilities ====
17140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
17240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    private static String getText(IDocument document, Node node) throws Exception {
17340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        IndexedRegion region = (IndexedRegion) node;
17440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        // This often returns the wrong value:
17540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        //int length = region.getLength();
17640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        int length = region.getEndOffset() - region.getStartOffset();
17740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        return document.get(region.getStartOffset(), length);
17840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    }
17940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
18040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    private Pair<IDocument, UiElementNode> getNode(String filename, String targetId)
18140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            throws Exception, PartInitException {
18240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        IFile file = getLayoutFile(getProject(), filename);
18340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        AdtPlugin.openFile(file, null);
184401e50139efc9333a689c2306ca66a7b2c840c40Tor Norbye        IEditorPart newEditor = AdtUtils.getActiveEditor();
18540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertTrue(newEditor instanceof AndroidXmlEditor);
18640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        AndroidXmlEditor xmlEditor = (AndroidXmlEditor) newEditor;
18740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        IStructuredDocument document = xmlEditor.getStructuredDocument();
18840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        UiElementNode root = xmlEditor.getUiRootNode();
18940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertNotNull(root);
19040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        UiElementNode node = findById(root, targetId);
19140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertNotNull(node);
19240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        Pair<IDocument, UiElementNode> pair = Pair.<IDocument, UiElementNode>of(document, node);
19340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        return pair;
19440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    }
19540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
19640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    private static UiElementNode findById(UiElementNode node, String targetId) {
19740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertFalse(targetId.startsWith(NEW_ID_PREFIX));
19840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        assertFalse(targetId.startsWith(ID_PREFIX));
19940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
20040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        String id = node.getAttributeValue(ATTR_ID);
20140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        if (id != null && targetId.equals(BaseLayoutRule.stripIdPrefix(id))) {
20240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            return node;
20340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        }
20440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
20540c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        for (UiElementNode child : node.getUiChildren()) {
20640c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            UiElementNode result = findById(child, targetId);
20740c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            if (result != null) {
20840c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye                return result;
20940c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye            }
21040c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        }
21140c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye
21240c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye        return null;
21340c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye    }
21440c1413a861cefb3bfb2cd200c89ffdf84fdc308Tor Norbye}
215