163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye/*
263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye * Copyright (C) 2010 The Android Open Source Project
363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye *
463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye * Licensed under the Eclipse Public License, Version 1.0 (the "License");
563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye * you may not use this file except in compliance with the License.
663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye * You may obtain a copy of the License at
763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye *
863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye *      http://www.eclipse.org/org/documents/epl-v10.php
963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye *
1063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye * Unless required by applicable law or agreed to in writing, software
1163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye * distributed under the License is distributed on an "AS IS" BASIS,
1263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye * See the License for the specific language governing permissions and
1463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye * limitations under the License.
1563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye */
1663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyepackage com.android.ide.common.layout;
1763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
1813a9256cbb601228887eea910ce1dbd68a776b17Tor Norbyeimport static com.android.SdkConstants.ANDROID_URI;
1912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ANDROID_WIDGET_PREFIX;
2012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_ID;
21fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport static junit.framework.Assert.assertEquals;
2213a9256cbb601228887eea910ce1dbd68a776b17Tor Norbyeimport static junit.framework.Assert.assertNotNull;
2313a9256cbb601228887eea910ce1dbd68a776b17Tor Norbyeimport static junit.framework.Assert.assertTrue;
2413a9256cbb601228887eea910ce1dbd68a776b17Tor Norbyeimport static junit.framework.Assert.fail;
25bfcbad18934be1f51ae03d4b6255cfe2d7239aafTor Norbye
267e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbyeimport com.android.annotations.NonNull;
277e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbyeimport com.android.annotations.Nullable;
2863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyeimport com.android.ide.common.api.IAttributeInfo;
2963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyeimport com.android.ide.common.api.INode;
3063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyeimport com.android.ide.common.api.INodeHandler;
3180d9301c2e874b29889c41adb0623666cf534fa0Tor Norbyeimport com.android.ide.common.api.Margins;
3263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyeimport com.android.ide.common.api.Rect;
3313a9256cbb601228887eea910ce1dbd68a776b17Tor Norbyeimport com.android.ide.common.xml.XmlFormatStyle;
3413a9256cbb601228887eea910ce1dbd68a776b17Tor Norbyeimport com.android.ide.common.xml.XmlPrettyPrinter;
3513a9256cbb601228887eea910ce1dbd68a776b17Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlFormatPreferences;
3613a9256cbb601228887eea910ce1dbd68a776b17Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlPrettyPrinter;
37fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
38fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport com.google.common.base.Splitter;
3963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
400cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbyeimport org.w3c.dom.Attr;
41fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport org.w3c.dom.Document;
42fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport org.w3c.dom.Element;
430cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbyeimport org.w3c.dom.NamedNodeMap;
44fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
45fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport java.io.IOException;
46fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport java.io.StringWriter;
4763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyeimport java.util.ArrayList;
48e287bc8580e19d72a911615475b353790cf000ebTor Norbyeimport java.util.Collections;
4963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyeimport java.util.HashMap;
50fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport java.util.Iterator;
5163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyeimport java.util.List;
5263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyeimport java.util.Map;
53fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport java.util.regex.Matcher;
54fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbyeimport java.util.regex.Pattern;
55fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
5663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye/** Test/mock implementation of {@link INode} */
57fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye@SuppressWarnings("javadoc")
5863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbyepublic class TestNode implements INode {
5963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    private TestNode mParent;
6063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
6163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    private final List<TestNode> mChildren = new ArrayList<TestNode>();
6263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
6363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    private final String mFqcn;
6463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
6563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    private Rect mBounds = new Rect(); // Invalid bounds initially
6663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
6763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    private Map<String, IAttribute> mAttributes = new HashMap<String, IAttribute>();
6863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
6963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    private Map<String, IAttributeInfo> mAttributeInfos = new HashMap<String, IAttributeInfo>();
7063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
71e287bc8580e19d72a911615475b353790cf000ebTor Norbye    private List<String> mAttributeSources;
72e287bc8580e19d72a911615475b353790cf000ebTor Norbye
7363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public TestNode(String fqcn) {
7463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        this.mFqcn = fqcn;
7563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
7663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
7763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public TestNode bounds(Rect bounds) {
7863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        this.mBounds = bounds;
7963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
8063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return this;
8163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
8263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
8363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public TestNode id(String id) {
84bfcbad18934be1f51ae03d4b6255cfe2d7239aafTor Norbye        return set(ANDROID_URI, ATTR_ID, id);
8563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
8663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
8763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public TestNode set(String uri, String name, String value) {
8863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        setAttribute(uri, name, value);
8963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
9063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return this;
9163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
9263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
9363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public TestNode add(TestNode child) {
9463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        mChildren.add(child);
9563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        child.mParent = this;
9663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
9763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return this;
9863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
9963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
10063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public TestNode add(TestNode... children) {
10163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        for (TestNode child : children) {
10263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye            mChildren.add(child);
10363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye            child.mParent = this;
10463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        }
10563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
10663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return this;
10763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
10863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
10963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public static TestNode create(String fcqn) {
11063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return new TestNode(fcqn);
11163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
11263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
11363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public void removeChild(int index) {
11463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        TestNode removed = mChildren.remove(index);
11563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        removed.mParent = null;
11663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
11763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
11863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    // ==== INODE ====
11963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
120ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1217e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull INode appendChild(@NonNull String viewFqcn) {
12263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return insertChildAt(viewFqcn, mChildren.size());
12363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
12463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
125ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1267e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public void editXml(@NonNull String undoName, @NonNull INodeHandler callback) {
12763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        callback.handle(this);
12863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
12963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
130e287bc8580e19d72a911615475b353790cf000ebTor Norbye    public void putAttributeInfo(String uri, String attrName, IAttributeInfo info) {
131e287bc8580e19d72a911615475b353790cf000ebTor Norbye        mAttributeInfos.put(uri + attrName, info);
132e287bc8580e19d72a911615475b353790cf000ebTor Norbye    }
133e287bc8580e19d72a911615475b353790cf000ebTor Norbye
134ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1357e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public IAttributeInfo getAttributeInfo(@Nullable String uri, @NonNull String attrName) {
13663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return mAttributeInfos.get(uri + attrName);
13763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
13863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
139ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1407e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull Rect getBounds() {
14163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return mBounds;
14263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
14363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
144ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1457e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull INode[] getChildren() {
14663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return mChildren.toArray(new INode[mChildren.size()]);
14763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
14863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
149ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1507e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull IAttributeInfo[] getDeclaredAttributes() {
15163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return mAttributeInfos.values().toArray(new IAttributeInfo[mAttributeInfos.size()]);
15263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
15363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
154ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1557e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull String getFqcn() {
15663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return mFqcn;
15763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
15863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
159ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1607e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull IAttribute[] getLiveAttributes() {
16163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return mAttributes.values().toArray(new IAttribute[mAttributes.size()]);
16263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
16363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
164ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
16563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public INode getParent() {
16663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return mParent;
16763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
16863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
169ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
17063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public INode getRoot() {
17163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        TestNode curr = this;
17263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        while (curr.mParent != null) {
17363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye            curr = curr.mParent;
17463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        }
17563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
17663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return curr;
17763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
17863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
179ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1807e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public String getStringAttr(@Nullable String uri, @NonNull String attrName) {
18163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        IAttribute attr = mAttributes.get(uri + attrName);
18263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        if (attr == null) {
18363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye            return null;
18463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        }
18563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
18663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return attr.getValue();
18763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
18863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
189ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
1907e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull INode insertChildAt(@NonNull String viewFqcn, int index) {
19163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        TestNode child = new TestNode(viewFqcn);
19263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        if (index == -1) {
19363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye            mChildren.add(child);
19463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        } else {
19563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye            mChildren.add(index, child);
19663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        }
19763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        child.mParent = this;
19863a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return child;
19963a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
20063a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
201ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
2027e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public void removeChild(@NonNull INode node) {
203adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        int index = mChildren.indexOf(node);
204adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        if (index != -1) {
205adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            removeChild(index);
206adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        }
207adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye    }
208adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye
209ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
2107e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public boolean setAttribute(@Nullable String uri, @NonNull String localName,
2117e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye            @Nullable String value) {
21263a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        mAttributes.put(uri + localName, new TestAttribute(uri, localName, value));
21363a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye        return true;
21463a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
21563a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye
21663a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    @Override
21763a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    public String toString() {
218fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        String id = getStringAttr(ANDROID_URI, ATTR_ID);
219fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        return "TestNode [id=" + (id != null ? id : "?") + ", fqn=" + mFqcn + ", infos="
220fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                + mAttributeInfos + ", attributes=" + mAttributes + ", bounds=" + mBounds + "]";
22163a966c7d8a08f175d1a8aee688c74d0090af888Tor Norbye    }
222adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye
223ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
22480d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye    public int getBaseline() {
22580d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye        return -1;
22680d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye    }
22780d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye
228ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
2297e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull Margins getMargins() {
23080d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye        return null;
23180d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye    }
232e287bc8580e19d72a911615475b353790cf000ebTor Norbye
233ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
2347e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull List<String> getAttributeSources() {
235e287bc8580e19d72a911615475b353790cf000ebTor Norbye        return mAttributeSources != null ? mAttributeSources : Collections.<String>emptyList();
236e287bc8580e19d72a911615475b353790cf000ebTor Norbye    }
237e287bc8580e19d72a911615475b353790cf000ebTor Norbye
238e287bc8580e19d72a911615475b353790cf000ebTor Norbye    public void setAttributeSources(List<String> attributeSources) {
239e287bc8580e19d72a911615475b353790cf000ebTor Norbye        mAttributeSources = attributeSources;
240e287bc8580e19d72a911615475b353790cf000ebTor Norbye    }
241fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
242fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    /** Create a test node from the given XML */
243fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    public static TestNode createFromXml(String xml) {
244fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        Document document = DomUtilities.parseDocument(xml, false);
245fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        assertNotNull(document);
246fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        assertNotNull(document.getDocumentElement());
247fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
248fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        return createFromNode(document.getDocumentElement());
249fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
250fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
251fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    public static String toXml(TestNode node) {
252fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        assertTrue("This method only works with nodes constructed from XML",
253fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                node instanceof TestXmlNode);
254fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        Document document = ((TestXmlNode) node).mElement.getOwnerDocument();
255fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        // Insert new whitespace nodes etc
256fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        String xml = dumpDocument(document);
257fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        document = DomUtilities.parseDocument(xml, false);
258fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
25913a9256cbb601228887eea910ce1dbd68a776b17Tor Norbye        XmlPrettyPrinter printer = new EclipseXmlPrettyPrinter(EclipseXmlFormatPreferences.create(),
260fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                XmlFormatStyle.LAYOUT, "\n");
261fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        StringBuilder sb = new StringBuilder(1000);
262fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
263fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        printer.prettyPrint(-1, document, null, null, sb, false);
264fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        return sb.toString();
265fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
266fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
267fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    @SuppressWarnings("deprecation")
268fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    private static String dumpDocument(Document document) {
269fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        // Diagnostics: print out the XML that we're about to render
270fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        org.apache.xml.serialize.OutputFormat outputFormat =
271fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                new org.apache.xml.serialize.OutputFormat(
272fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                        "XML", "ISO-8859-1", true); //$NON-NLS-1$ //$NON-NLS-2$
273fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        outputFormat.setIndent(2);
274fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        outputFormat.setLineWidth(100);
275fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        outputFormat.setIndenting(true);
276fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        outputFormat.setOmitXMLDeclaration(true);
277fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        outputFormat.setOmitDocumentType(true);
278fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        StringWriter stringWriter = new StringWriter();
279fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        // Using FQN here to avoid having an import above, which will result
280fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        // in a deprecation warning, and there isn't a way to annotate a single
281fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        // import element with a SuppressWarnings.
282fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        org.apache.xml.serialize.XMLSerializer serializer =
283fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                new org.apache.xml.serialize.XMLSerializer(stringWriter, outputFormat);
284fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        serializer.setNamespaces(true);
285fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        try {
286fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            serializer.serialize(document.getDocumentElement());
287fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            return stringWriter.toString();
288fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        } catch (IOException e) {
289fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            e.printStackTrace();
290fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
291fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        return null;
292fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
293fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
294fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    private static TestNode createFromNode(Element element) {
295fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        String fqcn = ANDROID_WIDGET_PREFIX + element.getTagName();
296fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        TestNode node = new TestXmlNode(fqcn, element);
297fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
298fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        for (Element child : DomUtilities.getChildren(element)) {
299fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            node.add(createFromNode(child));
300fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
301fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
302fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        return node;
303fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
304fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
305fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    @Nullable
306fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    public static TestNode findById(TestNode node, String id) {
307fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        id = BaseLayoutRule.stripIdPrefix(id);
308fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        return node.findById(id);
309fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
310fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
311fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    private TestNode findById(String targetId) {
312fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        String id = getStringAttr(ANDROID_URI, ATTR_ID);
313fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        if (id != null && targetId.equals(BaseLayoutRule.stripIdPrefix(id))) {
314fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            return this;
315fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
316fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
317fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        for (TestNode child : mChildren) {
318fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            TestNode result = child.findById(targetId);
319fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            if (result != null) {
320fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                return result;
321fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            }
322fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
323fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
324fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        return null;
325fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
326fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
327fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    private static String getTagName(String fqcn) {
328fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        return fqcn.substring(fqcn.lastIndexOf('.') + 1);
329fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
330fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
331fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    private static class TestXmlNode extends TestNode {
332fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        private final Element mElement;
333fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
334fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        public TestXmlNode(String fqcn, Element element) {
335fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            super(fqcn);
336fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            mElement = element;
337fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
338fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
339fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        @Override
3400cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        public @NonNull IAttribute[] getLiveAttributes() {
3410cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            List<IAttribute> result = new ArrayList<IAttribute>();
3420cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye
3430cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            NamedNodeMap attributes = mElement.getAttributes();
3440cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            for (int i = 0, n = attributes.getLength(); i < n; i++) {
3450cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye                Attr attribute = (Attr) attributes.item(i);
3460cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye                result.add(new TestXmlAttribute(attribute));
3470cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            }
3480cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            return result.toArray(new IAttribute[result.size()]);
3490cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        }
3500cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye
3510cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        @Override
352fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        public boolean setAttribute(String uri, String localName, String value) {
3530cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            if (value == null) {
3540cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye                mElement.removeAttributeNS(uri, localName);
3550cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            } else {
3560cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye                mElement.setAttributeNS(uri, localName, value);
3570cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            }
358fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            return super.setAttribute(uri, localName, value);
359fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
360fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
361fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        @Override
362fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        public INode appendChild(String viewFqcn) {
363fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            Element child = mElement.getOwnerDocument().createElement(getTagName(viewFqcn));
364fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            mElement.appendChild(child);
365fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            return new TestXmlNode(viewFqcn, child);
366fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
367fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
368fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        @Override
369fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        public INode insertChildAt(String viewFqcn, int index) {
370fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            if (index == -1) {
371fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                return appendChild(viewFqcn);
372fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            }
373fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            Element child = mElement.getOwnerDocument().createElement(getTagName(viewFqcn));
374fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            List<Element> children = DomUtilities.getChildren(mElement);
375fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            if (children.size() >= index) {
376fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                Element before = children.get(index);
377fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                mElement.insertBefore(child, before);
378fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            } else {
379fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                fail("Unexpected index");
380fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                mElement.appendChild(child);
381fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            }
382fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            return new TestXmlNode(viewFqcn, child);
383fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
384fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
385fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        @Override
386fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        public String getStringAttr(String uri, String name) {
387fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            String value;
388fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            if (uri == null) {
389fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                value = mElement.getAttribute(name);
390fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            } else {
391fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                value = mElement.getAttributeNS(uri, name);
392fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            }
393fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            if (value.isEmpty()) {
394fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye                value = null;
395fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            }
396fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
397fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            return value;
398fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
399fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
400fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        @Override
401fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        public void removeChild(INode node) {
402fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            assert node instanceof TestXmlNode;
403fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            mElement.removeChild(((TestXmlNode) node).mElement);
404fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
405fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
406fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        @Override
407fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        public void removeChild(int index) {
408fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            List<Element> children = DomUtilities.getChildren(mElement);
409fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            assertTrue(index < children.size());
410fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            Element oldChild = children.get(index);
411fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            mElement.removeChild(oldChild);
412fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
413fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
414fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
4150cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye    public static class TestXmlAttribute implements IAttribute {
4160cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        private Attr mAttribute;
4170cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye
4180cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        public TestXmlAttribute(Attr attribute) {
4190cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            this.mAttribute = attribute;
4200cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        }
4210cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye
4220cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        @Override
4230cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        public String getUri() {
4240cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            return mAttribute.getNamespaceURI();
4250cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        }
4260cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye
4270cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        @Override
4280cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        public String getName() {
4290cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            String name = mAttribute.getLocalName();
4300cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            if (name == null) {
4310cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye                name = mAttribute.getName();
4320cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            }
4330cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            return name;
4340cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        }
4350cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye
4360cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        @Override
4370cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        public String getValue() {
4380cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye            return mAttribute.getValue();
4390cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye        }
4400cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye    }
4410cb8e647ef345f5c2a6b7eb08b517421131bca4eTor Norbye
442fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    // Recursively initialize this node with the bounds specified in the given hierarchy
443fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    // dump (from ViewHierarchy's DUMP_INFO flag
444fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    public void assignBounds(String bounds) {
445fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        Iterable<String> split = Splitter.on('\n').trimResults().split(bounds);
446fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        assignBounds(split.iterator());
447fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
448fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
449fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    private void assignBounds(Iterator<String> iterator) {
450fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        assertTrue(iterator.hasNext());
451fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        String desc = iterator.next();
452fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
453fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        Pattern pattern = Pattern.compile("^\\s*(.+)\\s+\\[(.+)\\]\\s*(<.+>)?\\s*(\\S+)?\\s*$");
454fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        Matcher matcher = pattern.matcher(desc);
455fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        assertTrue(matcher.matches());
456fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        String fqn = matcher.group(1);
457fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        assertEquals(getFqcn(), fqn);
458fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        String boundsString = matcher.group(2);
459fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        String[] bounds = boundsString.split(",");
460fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        assertEquals(boundsString, 4, bounds.length);
461fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        try {
462fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            int left = Integer.parseInt(bounds[0]);
463fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            int top = Integer.parseInt(bounds[1]);
464fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            int right = Integer.parseInt(bounds[2]);
465fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            int bottom = Integer.parseInt(bounds[3]);
466fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            mBounds = new Rect(left, top, right - left, bottom - top);
467fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        } catch (NumberFormatException nufe) {
46813a9256cbb601228887eea910ce1dbd68a776b17Tor Norbye            fail(nufe.getLocalizedMessage());
469fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
470fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        String tag = matcher.group(3);
471fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye
472fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        for (INode child : getChildren()) {
473fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            assertTrue(iterator.hasNext());
474fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye            ((TestNode) child).assignBounds(iterator);
475fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye        }
476fb6d52d71a7461cb1ac4149f4f71d6a63e7436daTor Norbye    }
477a881b0b34678ad76c9f5eba62fac7a00a22ac606Tor Norbye}
478