All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.resources.base.CommentTrackingXmlPullParser Maven / Gradle / Ivy

/*
 * Copyright (C) 2019 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.resources.base;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;

import static com.android.SdkConstants.TAG_EAT_COMMENT;

/**
 * An {@link XmlPullParser} that keeps track of the last comment preceding an XML tag and special comments
 * that are used in the framework resource files for describing groups of "attr" resources. Here is
 * an example of an "attr" group comment:
 * 
 *   <!-- =========== -->
 *   <!-- Text styles -->
 *   <!-- =========== -->
 *   <eat-comment/>
 * 
*/ public class CommentTrackingXmlPullParser extends KXmlParser { // Used for parsing group of attributes, used heuristically to skip long comments before . private static final int ATTR_GROUP_MAX_CHARACTERS = 40; @Nullable String myLastComment; boolean tagEncounteredAfterComment; @NotNull final ArrayList myAttrGroupCommentStack = new ArrayList<>(4); /** * Initializes the parser. XML namespaces are supported by default. */ public CommentTrackingXmlPullParser() { try { setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); } catch (XmlPullParserException e) { throw new Error(e); // KXmlParser is guaranteed to support FEATURE_PROCESS_NAMESPACES. } } /** * Returns the last encountered comment that is not an ASCII art. */ @Nullable public String getLastComment() { return myLastComment; } /** * Returns the name of the current "attr" group, e.g. "Button Styles" group for "buttonStyleSmall" "attr" tag. */ @Nullable public String getAttrGroupComment() { return myAttrGroupCommentStack.get(myAttrGroupCommentStack.size() - 1); } @Override public int nextToken() throws XmlPullParserException, IOException { int token = super.nextToken(); processToken(token); return token; } @Override public int next() throws XmlPullParserException, IOException { throw new UnsupportedOperationException("Use nextToken() instead of next() for comment tracking to work"); } private void processToken(int token) { switch (token) { case XmlPullParser.START_TAG: if (tagEncounteredAfterComment) { myLastComment = null; } tagEncounteredAfterComment = true; // Duplicate the last element in myAttrGroupCommentStack. myAttrGroupCommentStack.add(myAttrGroupCommentStack.get(myAttrGroupCommentStack.size() - 1)); assert myAttrGroupCommentStack.size() == getDepth() + 1; if (TAG_EAT_COMMENT.equals(getName()) && getPrefix() == null) { // The framework attribute file follows a special convention where related attributes are grouped together, // and there is always a set of comments that indicate these sections which look like this: // // // // // These section headers are always immediately followed by an . Not all sections are // actually attribute headers, some are comments. We identify these by looking at the line length; category comments // are short, and descriptive comments are longer. if (myLastComment != null && myLastComment.length() <= ATTR_GROUP_MAX_CHARACTERS && !myLastComment.startsWith("TODO:")) { String attrGroupComment = myLastComment; if (attrGroupComment.endsWith(".")) { attrGroupComment = attrGroupComment.substring(0, attrGroupComment.length() - 1); // Strip the trailing period. } // Replace the second to last element in myAttrGroupCommentStack. myAttrGroupCommentStack.set(myAttrGroupCommentStack.size() - 2, attrGroupComment); } } break; case XmlPullParser.END_TAG: myLastComment = null; myAttrGroupCommentStack.remove(myAttrGroupCommentStack.size() - 1); break; case XmlPullParser.COMMENT: { String commentText = getText().trim(); if (!isEmptyOrAsciiArt(commentText)) { myLastComment = commentText; tagEncounteredAfterComment = false; } break; } } } @Override public void setInput(@NotNull Reader reader) throws XmlPullParserException { super.setInput(reader); myLastComment = null; myAttrGroupCommentStack.clear(); myAttrGroupCommentStack.add(null); } @Override public void setInput(@NotNull InputStream inputStream, @Nullable String encoding) throws XmlPullParserException { super.setInput(inputStream, encoding); myLastComment = null; myAttrGroupCommentStack.clear(); myAttrGroupCommentStack.add(null); } private static boolean isEmptyOrAsciiArt(@NotNull String commentText) { return commentText.isEmpty() || commentText.charAt(0) == '*' || commentText.charAt(0) == '='; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy