blob: d2c4343912b3c39c860bfe8b18a7e2f10ff820a5 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.intellij.util.xml;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.util.containers.Stack;
import com.intellij.util.text.StringFactory;
import net.n3.nanoxml.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.Properties;
/**
* @author mike
*/
public class NanoXmlUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.NanoXmlUtil");
private NanoXmlUtil() {
}
private static MyXMLReader createReader(PsiFile psiFile) {
return new MyXMLReader(new StringReader(psiFile.getText()));
}
public static void parseFile(PsiFile psiFile, final IXMLBuilder builder) {
MyXMLReader reader = createReader(psiFile);
parse(reader, builder);
}
public static void parse(final InputStream is, final IXMLBuilder builder) {
try {
parse(new MyXMLReader(is), builder);
}
catch(IOException e) {
LOG.error(e);
}
finally {
try {
is.close();
}
catch (IOException ignore) {
}
}
}
public static void parse(final Reader reader, final IXMLBuilder builder) {
parse(reader, builder, null);
}
public static void parse(final Reader reader, final IXMLBuilder builder, @Nullable final IXMLValidator validator) {
try {
parse(new MyXMLReader(reader), builder, validator);
}
catch (Exception e) {
LOG.error(e);
}
finally {
try {
reader.close();
}
catch (IOException ignore) {
}
}
}
public static void parse(final IXMLReader r, final IXMLBuilder builder) {
parse(r, builder, null);
}
public static void parse(final IXMLReader r, final IXMLBuilder builder, @Nullable final IXMLValidator validator) {
try {
final IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
parser.setReader(r);
parser.setBuilder(builder);
parser.setValidator(validator == null ? new EmptyValidator() : validator);
parser.setResolver(new EmptyEntityResolver());
try {
parser.parse();
}
catch (XMLException e) {
if (e.getException() instanceof ParserStoppedException) return;
if (e.getException() instanceof ParserStoppedXmlException) return;
LOG.debug(e);
}
}
catch (ClassNotFoundException e) {
LOG.error(e);
}
catch (InstantiationException e) {
LOG.error(e);
}
catch (IllegalAccessException e) {
LOG.error(e);
}
}
@NotNull
public static XmlFileHeader parseHeader(VirtualFile file) {
try {
return parseHeaderWithException(file);
}
catch (IOException e) {
LOG.error(e);
return null;
}
}
@NotNull
public static XmlFileHeader parseHeaderWithException(Reader reader) throws IOException {
return parseHeader(new MyXMLReader(reader));
}
@NotNull
public static XmlFileHeader parseHeaderWithException(final VirtualFile file) throws IOException {
final InputStream stream = file.getInputStream();
try {
return parseHeader(new MyXMLReader(stream));
}
finally {
stream.close();
}
}
@Deprecated // TODO: remove
@NotNull
public static XmlFileHeader parseHeader(final InputStream inputStream) {
try {
return parseHeader(new MyXMLReader(inputStream));
}
catch (IOException e) {
LOG.error(e);
return null;
}
}
@NotNull
public static XmlFileHeader parseHeader(final Reader reader) {
return parseHeader(new MyXMLReader(reader));
}
@NotNull
public static XmlFileHeader parseHeader(PsiFile file) {
return parseHeader(createReader(file));
}
@NotNull
private static XmlFileHeader parseHeader(final MyXMLReader r) {
final RootTagInfoBuilder builder = new RootTagInfoBuilder();
parse(r, builder);
return new XmlFileHeader(builder.getRootTagName(), builder.getNamespace(), r.publicId, r.systemId);
}
public static String createLocation(@NonNls String ...tagNames) {
StringBuilder result = new StringBuilder();
for (String tagName : tagNames) {
result.append(".");
result.append(tagName);
}
return result.toString();
}
public static class IXMLBuilderAdapter implements IXMLBuilder {
@Override
public void startBuilding(final String systemID, final int lineNr) throws Exception {
}
@Override
public void newProcessingInstruction(final String target, final Reader reader) throws Exception {
}
@Override
public void startElement(final String name, final String nsPrefix, final String nsURI, final String systemID, final int lineNr)
throws Exception {
}
@Override
public void addAttribute(final String key, final String nsPrefix, final String nsURI, final String value, final String type)
throws Exception {
}
@Override
public void elementAttributesProcessed(final String name, final String nsPrefix, final String nsURI) throws Exception {
}
@Override
public void endElement(final String name, final String nsPrefix, final String nsURI) throws Exception {
}
@Override
public void addPCData(final Reader reader, final String systemID, final int lineNr) throws Exception {
}
@Override
@Nullable
public Object getResult() throws Exception {
return null;
}
protected static void stop() throws ParserStoppedXmlException {
throw ParserStoppedXmlException.INSTANCE;
}
}
public static class BaseXmlBuilder extends IXMLBuilderAdapter {
private final Stack<String> myLocation = new Stack<String>();
@Override
public void startBuilding(String systemID, int lineNr) throws Exception {
myLocation.push("");
}
@Override
public void startElement(String name, String nsPrefix, String nsURI, String systemID, int lineNr) throws Exception {
myLocation.push(myLocation.peek() + "." + name);
}
@Override
public void endElement(String name, String nsPrefix, String nsURI) throws Exception {
myLocation.pop();
}
protected static String readText(final Reader reader) throws IOException {
return StringFactory.createShared(StreamUtil.readTextAndConvertSeparators(reader));
}
protected String getLocation() {
return myLocation.peek();
}
}
public static class EmptyValidator extends NonValidator {
private IXMLEntityResolver myParameterEntityResolver;
@Override
public void setParameterEntityResolver(IXMLEntityResolver resolver) {
myParameterEntityResolver = resolver;
}
@Override
public IXMLEntityResolver getParameterEntityResolver() {
return myParameterEntityResolver;
}
@Override
public void parseDTD(String publicID, IXMLReader reader, IXMLEntityResolver entityResolver, boolean external) throws Exception {
if (!external) {
//super.parseDTD(publicID, reader, entityResolver, external);
int cnt = 1;
for (char ch = reader.read(); !(ch == ']' && --cnt == 0); ch = reader.read()) {
if (ch == '[') cnt ++;
}
}
else {
int origLevel = reader.getStreamLevel();
while (true) {
char ch = reader.read();
if (reader.getStreamLevel() < origLevel) {
reader.unread(ch);
return; // end external DTD
}
}
}
}
@Override
public void elementStarted(String name, String systemId, int lineNr) {
}
@Override
public void elementEnded(String name, String systemId, int lineNr) {
}
@Override
public void attributeAdded(String key, String value, String systemId, int lineNr) {
}
@Override
public void elementAttributesProcessed(String name, Properties extraAttributes, String systemId, int lineNr) {
}
@Override
public void PCDataAdded(String systemId, int lineNr) {
}
}
private static class EmptyEntityResolver implements IXMLEntityResolver {
@Override
public void addInternalEntity(String name, String value) {
}
@Override
public void addExternalEntity(String name, String publicID, String systemID) {
}
@Override
public Reader getEntity(IXMLReader xmlReader, String name) throws XMLParseException {
return new StringReader("");
}
@Override
public boolean isExternalEntity(String name) {
return false;
}
}
private static class MyXMLReader extends StdXMLReader {
private String publicId;
private String systemId;
public MyXMLReader(final Reader documentReader) {
super(documentReader);
}
public MyXMLReader(InputStream stream) throws IOException {
super(stream);
}
@Override
public Reader openStream(String publicId, String systemId) throws IOException {
this.publicId = StringUtil.isEmpty(publicId) ? null : publicId;
this.systemId = StringUtil.isEmpty(systemId) ? null : systemId;
return new StringReader(" ");
}
}
public static class ParserStoppedXmlException extends XMLException {
public static final ParserStoppedException INSTANCE = new ParserStoppedException();
private ParserStoppedXmlException() {
super("Parsing stopped");
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
/**
* @deprecated throw {@link ParserStoppedXmlException#INSTANCE} instead
*/
public static class ParserStoppedException extends RuntimeException {
@Override
public Throwable fillInStackTrace() {
return this;
}
}
private static class RootTagInfoBuilder implements IXMLBuilder {
private String myRootTagName;
private String myNamespace;
@Override
public void startBuilding(final String systemID, final int lineNr) throws Exception {
}
@Override
public void newProcessingInstruction(final String target, final Reader reader) throws Exception {
}
@Override
public void startElement(final String name, final String nsPrefix, final String nsURI, final String systemID, final int lineNr) throws Exception {
myRootTagName = name;
myNamespace = nsURI;
throw ParserStoppedXmlException.INSTANCE;
}
@Override
public void addAttribute(final String key, final String nsPrefix, final String nsURI, final String value, final String type) throws Exception {
}
@Override
public void elementAttributesProcessed(final String name, final String nsPrefix, final String nsURI) throws Exception {
}
@Override
public void endElement(final String name, final String nsPrefix, final String nsURI) throws Exception {
}
@Override
public void addPCData(final Reader reader, final String systemID, final int lineNr) throws Exception {
}
public String getNamespace() {
return myNamespace;
}
public String getRootTagName() {
return myRootTagName;
}
@Override
public String getResult() {
return myRootTagName;
}
}
}