/* * Copyright (C) 2022 github.com/REAndroid * * 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.reandroid.xml.parser; import com.reandroid.xml.*; import java.io.*; import java.util.ArrayList; import java.util.List; public class XMLDocumentParser { private final XmlPullParser mParser; private XMLDocument mResDocument; private XMLElement mCurrentElement; private boolean mNameSpaceCreated; private StringBuilder mCurrentText; private List mComments; private int mIndent; public XMLDocumentParser(XmlPullParser parser){ this.mParser=parser; } public XMLDocumentParser(InputStream in) throws XMLParseException { this(createParser(in)); } public XMLDocumentParser(File file) throws XMLParseException { this(createParser(file)); } public XMLDocumentParser(String text) throws XMLParseException { this(createParser(text)); } public XMLDocument parse() throws XMLParseException { try { XMLDocument document= parseDocument(); close(); return document; } catch (XmlPullParserException | IOException e) { XMLParseException ex=new XMLParseException(e.getMessage()); ex.setStackTrace(e.getStackTrace()); throw ex; } } private void close(){ closeReader(); closeFileInputStream(); mResDocument=null; mCurrentElement=null; mCurrentText=null; mComments=null; } private void closeFileInputStream(){ if(!(mParser instanceof MXParser)){ return; } MXParser parser=(MXParser) mParser; InputStream inputStream = parser.getInputStream(); if(!(inputStream instanceof FileInputStream)){ return; } try { inputStream.close(); } catch (IOException ignored) { } } private void closeReader(){ if(!(mParser instanceof MXParser)){ return; } MXParser parser=(MXParser) mParser; Reader reader = parser.getReader(); if(reader!=null){ try { reader.close(); } catch (IOException ignored) { } } } private XMLDocument parseDocument() throws XmlPullParserException, IOException { mResDocument=null; int type; while ((type=mParser.nextToken()) !=XmlPullParser.END_DOCUMENT){ event(type); } event(XmlPullParser.END_DOCUMENT); if(mResDocument==null){ throw new XmlPullParserException("Failed to parse/empty document"); } return mResDocument; } private void event(int type) { if (type == XmlPullParser.START_DOCUMENT){ onStartDocument(); }else if (type == XmlPullParser.END_DOCUMENT){ onEndDocument(); }else if (type == XmlPullParser.START_TAG){ onStartTag(); }else if (type == XmlPullParser.END_TAG){ onEndTag(); }else if (type == XmlPullParser.TEXT){ onText(); }else if (type == XmlPullParser.ENTITY_REF){ onEntityRef(); }else if (type == XmlPullParser.COMMENT){ onComment(); }else if (type == XmlPullParser.IGNORABLE_WHITESPACE){ onIgnorableWhiteSpace(); }else { onUnknownType(type); } } private void onStartDocument(){ mResDocument=new XMLDocument(); mIndent=-1; } private void onEndDocument(){ flushComments(null); applyIndent(mResDocument); } private void onStartTag(){ String name=mParser.getName(); flushTextContent(); if(mCurrentElement==null){ if(mResDocument==null){ onStartDocument(); } mCurrentElement=new XMLElement(name); mResDocument.setDocumentElement(mCurrentElement); }else { mCurrentElement=mCurrentElement.createElement(name); } mCurrentElement.setColumnNumber(mParser.getColumnNumber()); mCurrentElement.setLineNumber(mParser.getLineNumber()); checkIndent(); flushComments(mCurrentElement); String ns=mParser.getNamespace(); if(!XMLUtil.isEmpty(ns)){ String prefix=mParser.getPrefix(); if(!XMLUtil.isEmpty(prefix)){ String tagName=appendPrefix(prefix,name); mCurrentElement.setTagName(tagName); checkNamespace(prefix, ns); } } loadAttributes(); } private void loadAttributes(){ int max=mParser.getAttributeCount(); for(int i=0; i(); } mComments.add(ce); } private void flushComments(XMLElement element){ if(mComments==null){ return; } if(element!=null){ element.addComments(mComments); } mComments.clear(); mComments=null; } private void onIgnorableWhiteSpace(){ } private void onIgnore(int type){ } private void onUnknownType(int type){ String typeName=toTypeName(type); //System.err.println("Unknown TYPE = "+typeName+" "+type); } private String toTypeName(int type){ String[] allTypes=XmlPullParser.TYPES; if(type<0 || type>=allTypes.length){ return "type:"+type; } return allTypes[type]; } private void checkIndent(){ if(mIndent>=0){ return; } String txt=mParser.getText(); if(txt==null){ return; } int len=txt.length(); int col=mParser.getColumnNumber(); mIndent=col-len; if(mIndent<0){ mIndent=0; } } private void applyIndent(XMLDocument resDocument){ if(mIndent<=0 || mIndent>5 || resDocument==null){ mIndent=-1; return; } resDocument.setIndent(mIndent); mIndent=-1; } private static XmlPullParser createParser(String text) throws XMLParseException { if(text == null){ throw new XMLParseException("Text is null, failed to create XmlPullParser"); } InputStream in = new ByteArrayInputStream(text.getBytes()); return createParser(in); } private static XmlPullParser createParser(File file) throws XMLParseException { if(file == null){ throw new XMLParseException("File is null, failed to create XmlPullParser"); } if(!file.isFile()){ throw new XMLParseException("No such file : "+file.getAbsolutePath()); } InputStream in; try { in=new FileInputStream(file); return createParser(in); } catch (FileNotFoundException e) { throw new XMLParseException(e.getMessage()); } } private static XmlPullParser createParser(InputStream in) throws XMLParseException { try { XmlPullParser parser = new MXParserNonValidating(); parser.setInput(in, null); return parser; } catch (XmlPullParserException e) { throw new XMLParseException(e.getMessage()); } } private static boolean isAndroid(int id){ int pkgId=toPackageId(id); return pkgId>0 && pkgId<=ANDROID_PACKAGE_MAX; } private static boolean isResourceId(int id){ int pkgId=toPackageId(id); return pkgId>0 && pkgId<128; } private static int toPackageId(int id){ if(id<=0xff){ return id; } return ((id >> 24) & 0xff); } private static final int ANDROID_PACKAGE_MAX=3; }