/* * Sonar XML Plugin * Copyright (C) 2010 Matthijs Galesloot * dev@sonar.codehaus.org * * 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 org.sonar.plugins.xml.checks; import org.apache.commons.lang.StringUtils; import org.sonar.check.Cardinality; import org.sonar.check.Priority; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; import org.sonar.plugins.xml.parsers.SaxParser; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * Perform check for indenting of elements. * * @author Matthijs Galesloot * @since 1.0 */ @Rule(key = "IndentCheck", name = "Indent Check", description = "Indent Check", priority = Priority.MINOR, cardinality = Cardinality.SINGLE) public class IndentCheck extends AbstractPageCheck { @RuleProperty(key = "indentSize", description = "Indent Size", defaultValue = "2") private int indentSize = 2; @RuleProperty(key = "tabSize", description = "Tab Size", defaultValue = "2") private int tabSize = 2; /** * Collect the indenting whitespace before this node. */ private int collectIndent(Node node) { int indent = 0; for (Node sibling = node.getPreviousSibling(); sibling != null; sibling = sibling.getPreviousSibling()) { switch (sibling.getNodeType()) { case Node.COMMENT_NODE: case Node.ELEMENT_NODE: return indent; case Node.TEXT_NODE: String text = sibling.getTextContent(); if (StringUtils.isWhitespace(text)) { for (int i = text.length() - 1; i >= 0; i--) { char c = text.charAt(i); switch (c) { case '\n': // newline found, we are done return indent; case '\t': // add tabsize indent += tabSize; break; case ' ': // add one space indent++; break; default: break; } } } else { // non whitespace found, we are done return indent; } break; default: break; } } return indent; } /** * Get the depth of this node in the node hierarchy. */ private int getDepth(Node node) { int depth = 0; for (Node parent = node.getParentNode(); parent.getParentNode() != null; parent = parent.getParentNode()) { depth++; } return depth; } public int getIndentSize() { return indentSize; } public int getTabSize() { return tabSize; } public void setIndentSize(int indentSize) { this.indentSize = indentSize; } public void setTabSize(int tabSize) { this.tabSize = tabSize; } @Override public void validate(XmlSourceCode xmlSourceCode) { setWebSourceCode(xmlSourceCode); Document document = getWebSourceCode().getDocument(false); if (document != null && document.getDocumentElement() != null) { validateIndent(document.getDocumentElement()); } } /** * Validate the indent for this node. */ private void validateIndent(Node node) { int depth = getDepth(node); int indent = collectIndent(node); if (depth * indentSize != indent) { createViolation(SaxParser.getLineNumber(node), "Wrong indentation"); } // check the child elements for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { switch (child.getNodeType()) { case Node.ELEMENT_NODE: case Node.COMMENT_NODE: validateIndent(child); break; default: break; } } } }