| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.tools.idea.rendering; |
| |
| import com.android.resources.ResourceFolderType; |
| import com.android.utils.XmlUtils; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.xml.XmlAttribute; |
| import com.intellij.psi.xml.XmlFile; |
| import com.intellij.psi.xml.XmlTag; |
| import org.jetbrains.android.AndroidTestCase; |
| import org.jetbrains.annotations.Nullable; |
| import org.kxml2.io.KXmlParser; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import java.io.StringReader; |
| import java.util.SortedSet; |
| import java.util.TreeSet; |
| |
| import static com.android.SdkConstants.VALUE_FILL_PARENT; |
| import static com.android.SdkConstants.VALUE_MATCH_PARENT; |
| |
| /** |
| * Tests the parser by constructing a DOM from an XML file, and then it runs an XmlPullParser |
| * in parallel with this parser and checks event for event that the two parsers are returning the same results |
| */ |
| public class DomPullParserTest extends AndroidTestCase { |
| @SuppressWarnings("SpellCheckingInspection") |
| public DomPullParserTest() { |
| } |
| |
| public void test1() throws Exception { |
| //noinspection SpellCheckingInspection |
| checkFile("xmlpull/layout2.xml", ResourceFolderType.LAYOUT); |
| } |
| |
| enum NextEventType { NEXT, NEXT_TOKEN, NEXT_TAG } |
| |
| private static void compareParsers(PsiFile file, NextEventType nextEventType) throws Exception { |
| assertTrue(file instanceof XmlFile); |
| Document document = XmlUtils.parseDocumentSilently(file.getText(), true); |
| assertNotNull(document); |
| KXmlParser referenceParser = createReferenceParser(file); |
| DomPullParser parser = new DomPullParser(document.getDocumentElement()); |
| |
| assertEquals("Expected " + name(referenceParser.getEventType()) + " but was " |
| + name(parser.getEventType()) |
| + " (at line:column " + describePosition(referenceParser) + ")", |
| referenceParser.getEventType(), parser.getEventType()); |
| |
| while (true) { |
| int expected, next; |
| switch (nextEventType) { |
| case NEXT: |
| expected = referenceParser.next(); |
| next = parser.next(); |
| break; |
| case NEXT_TOKEN: |
| expected = referenceParser.nextToken(); |
| next = parser.nextToken(); |
| break; |
| case NEXT_TAG: { |
| try { |
| expected = referenceParser.nextTag(); |
| } catch (Exception e) { |
| expected = referenceParser.getEventType(); |
| } |
| try { |
| next = parser.nextTag(); |
| } catch (Exception e) { |
| next = parser.getEventType(); |
| } |
| break; |
| } |
| default: |
| fail("Unexpected type"); |
| return; |
| } |
| |
| Element element = null; |
| if (expected == XmlPullParser.START_TAG) { |
| assertNotNull(parser.getViewKey()); |
| assertNotNull(parser.getViewCookie()); |
| assertTrue(parser.getViewCookie() == null || parser.getViewCookie() instanceof Element); |
| element = (Element)parser.getViewCookie(); |
| } |
| |
| if (expected == XmlPullParser.START_TAG) { |
| assertEquals(referenceParser.getName(), parser.getName()); |
| if (element != document.getDocumentElement()) { // KXmlParser seems to not include xmlns: attributes on the root tag! |
| SortedSet<String> referenceAttributes = new TreeSet<String>(); |
| SortedSet<String> attributes = new TreeSet<String>(); |
| for (int i = 0; i < referenceParser.getAttributeCount(); i++) { |
| String s = referenceParser.getAttributePrefix(i) + ':' + referenceParser.getAttributeName(i) + '=' |
| + referenceParser.getAttributeValue(i); |
| referenceAttributes.add(s); |
| } |
| for (int i = 0; i < parser.getAttributeCount(); i++) { |
| String s = parser.getAttributePrefix(i) + ':' + parser.getAttributeName(i) + '=' + parser.getAttributeValue(i); |
| attributes.add(s); |
| if (parser.getAttributeNamespace(i) != null) { |
| //noinspection ConstantConditions |
| assertEquals(normalizeValue(parser.getAttributeValue(i)), |
| normalizeValue(parser.getAttributeValue(parser.getAttributeNamespace(i), parser.getAttributeName(i)))); |
| } |
| } |
| |
| assertEquals(referenceAttributes, attributes); |
| } |
| assertEquals(referenceParser.isEmptyElementTag(), parser.isEmptyElementTag()); |
| |
| if (element instanceof XmlTag) { |
| XmlTag tag = (XmlTag)element; |
| for (XmlAttribute attribute : tag.getAttributes()) { |
| String namespace = attribute.getNamespace(); |
| String name = attribute.getLocalName(); |
| assertEquals(namespace + ':' + name + " in element " + parser.getName(), |
| normalizeValue(referenceParser.getAttributeValue(namespace, name)), |
| normalizeValue(parser.getAttributeValue(namespace, name))); |
| } |
| } |
| } else if (expected == XmlPullParser.TEXT || expected == XmlPullParser.COMMENT) { |
| assertEquals(StringUtil.notNullize(referenceParser.getText()).trim(), StringUtil.notNullize(parser.getText()).trim()); |
| } |
| |
| if (expected != next) { |
| assertEquals("Expected " + name(expected) + " but was " + name(next) |
| + "(At " + describePosition(referenceParser) + ")", |
| expected, next); |
| } |
| if (expected == XmlPullParser.END_DOCUMENT) { |
| break; |
| } |
| } |
| } |
| |
| @Nullable |
| private static String normalizeValue(@Nullable String value) { |
| // Some parser translate values; ensure that these are identical |
| if (value != null && value.equals(VALUE_MATCH_PARENT)) { |
| return VALUE_FILL_PARENT; |
| } |
| return value; |
| } |
| |
| private static String name(int event) { |
| return XmlPullParser.TYPES[event]; |
| } |
| |
| private void checkFile(String filename, ResourceFolderType folder) throws Exception { |
| VirtualFile file = myFixture.copyFileToProject(filename, "res/" + folder.getName() + "/" + filename.substring(filename.lastIndexOf('/') + 1)); |
| assertNotNull(file); |
| PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(file); |
| assertNotNull(psiFile); |
| compareParsers(psiFile, NextEventType.NEXT_TAG); |
| // The LayoutPsiPullParser only supports tags, not text (no text is used in layouts) |
| //compareParsers(psiFile, NextEventType.NEXT); |
| //compareParsers(psiFile, NextEventType.NEXT_TOKEN); |
| } |
| |
| private static KXmlParser createReferenceParser(PsiFile file) throws XmlPullParserException { |
| KXmlParser referenceParser = new KXmlParser(); |
| referenceParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); |
| referenceParser.setInput(new StringReader(file.getText())); |
| return referenceParser; |
| } |
| |
| private static String describePosition(KXmlParser referenceParser) { |
| return referenceParser.getLineNumber() + ":" + referenceParser.getColumnNumber(); |
| } |
| } |