Home | History | Annotate | Download | only in lint
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.tools.lint;
     18 
     19 import com.android.tools.lint.api.IDomParser;
     20 import com.android.tools.lint.detector.api.Context;
     21 import com.android.tools.lint.detector.api.Position;
     22 
     23 import org.w3c.dom.Attr;
     24 import org.w3c.dom.Document;
     25 import org.w3c.dom.Element;
     26 import org.w3c.dom.Node;
     27 import org.xml.sax.Attributes;
     28 import org.xml.sax.InputSource;
     29 import org.xml.sax.Locator;
     30 import org.xml.sax.SAXException;
     31 import org.xml.sax.XMLReader;
     32 import org.xml.sax.helpers.AttributesImpl;
     33 import org.xml.sax.helpers.XMLFilterImpl;
     34 import org.xml.sax.helpers.XMLReaderFactory;
     35 
     36 import java.io.StringReader;
     37 
     38 import javax.xml.transform.Transformer;
     39 import javax.xml.transform.TransformerConfigurationException;
     40 import javax.xml.transform.TransformerException;
     41 import javax.xml.transform.TransformerFactory;
     42 import javax.xml.transform.dom.DOMResult;
     43 import javax.xml.transform.sax.SAXSource;
     44 
     45 /** A simple XML parser which can store and retrieve line:column information for the nodes */
     46 public class PositionXmlParser implements IDomParser {
     47     private static final String ATTR_LOCATION = "location";                     //$NON-NLS-1$
     48     private static final String PRIVATE_NAMESPACE = "http://tools.android.com"; //$NON-NLS-1$
     49     private static final String PRIVATE_PREFIX = "temp";                        //$NON-NLS-1$
     50 
     51     public Document parse(Context context) {
     52         InputSource input = new InputSource(new StringReader(context.getContents()));
     53         try {
     54             Filter filter = new Filter(XMLReaderFactory.createXMLReader());
     55             Transformer transformer = TransformerFactory.newInstance().newTransformer();
     56             DOMResult result = new DOMResult();
     57             transformer.transform(new SAXSource(filter, input), result);
     58             return (Document) result.getNode();
     59         } catch (SAXException e) {
     60             // The file doesn't parse: not an exception. Infrastructure will log a warning
     61             // that this file was not analyzed.
     62             return null;
     63         } catch (TransformerConfigurationException e) {
     64             context.toolContext.log(e, null);
     65         } catch (TransformerException e) {
     66             context.toolContext.log(e, null);
     67         }
     68 
     69         return null;
     70     }
     71 
     72     private static class Filter extends XMLFilterImpl {
     73         private Locator mLocator;
     74 
     75         Filter(XMLReader reader) {
     76             super(reader);
     77         }
     78 
     79         @Override
     80         public void setDocumentLocator(Locator locator) {
     81             super.setDocumentLocator(locator);
     82             this.mLocator = locator;
     83         }
     84 
     85         @Override
     86         public void startElement(String uri, String localName, String qualifiedName,
     87                 Attributes attributes) throws SAXException {
     88             int lineno = mLocator.getLineNumber();
     89             int column = mLocator.getColumnNumber();
     90             String location = Integer.toString(lineno) + ':' + Integer.toString(column);
     91 
     92             // Modify attributes parameter to a copy that includes our private attribute
     93             AttributesImpl wrapper = new AttributesImpl(attributes);
     94             wrapper.addAttribute(PRIVATE_NAMESPACE, ATTR_LOCATION,
     95                     PRIVATE_PREFIX + ':' + ATTR_LOCATION, "CDATA", location); //$NON-NLS-1$
     96 
     97             super.startElement(uri, localName, qualifiedName, wrapper);
     98         }
     99     }
    100 
    101     public Position getStartPosition(Context context, Node node) {
    102         if (node instanceof Attr) {
    103             Attr attr = (Attr) node;
    104             node = attr.getOwnerElement();
    105         }
    106         if (node instanceof Element) {
    107             Attr attribute = ((Element) node).getAttributeNodeNS(PRIVATE_NAMESPACE, ATTR_LOCATION);
    108             if (attribute != null) {
    109                 String position = attribute.getValue();
    110                 int separator = position.indexOf(':');
    111                 int line = Integer.parseInt(position.substring(0, separator));
    112                 int column = Integer.parseInt(position.substring(separator + 1));
    113                 return new OffsetPosition(line, column, -1);
    114             }
    115         }
    116 
    117         return null;
    118     }
    119 
    120     public Position getEndPosition(Context context, Node node) {
    121         // TODO: Currently unused
    122         return null;
    123     }
    124 
    125     private static class OffsetPosition extends Position {
    126         /** The line number (0-based where the first line is line 0) */
    127         private final int mLine;
    128 
    129         /**
    130          * The column number (where the first character on the line is 0), or -1 if
    131          * unknown
    132          */
    133         private final int mColumn;
    134 
    135         /** The character offset */
    136         private final int mOffset;
    137 
    138         /**
    139          * Creates a new {@link Position}
    140          *
    141          * @param line the 0-based line number, or -1 if unknown
    142          * @param column the 0-based column number, or -1 if unknown
    143          * @param offset the offset, or -1 if unknown
    144          */
    145         public OffsetPosition(int line, int column, int offset) {
    146             this.mLine = line;
    147             this.mColumn = column;
    148             this.mOffset = offset;
    149         }
    150 
    151         @Override
    152         public int getLine() {
    153             return mLine;
    154         }
    155 
    156         @Override
    157         public int getOffset() {
    158             return mOffset;
    159         }
    160 
    161         @Override
    162         public int getColumn() {
    163             return mColumn;
    164         }
    165     }
    166 }
    167