001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2009 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.api.profiles; 021 022 import org.apache.commons.io.IOUtils; 023 import org.apache.commons.lang.CharEncoding; 024 import org.apache.commons.lang.StringUtils; 025 import org.codehaus.stax2.XMLInputFactory2; 026 import org.codehaus.staxmate.SMInputFactory; 027 import org.codehaus.staxmate.in.SMHierarchicCursor; 028 import org.codehaus.staxmate.in.SMInputCursor; 029 import org.sonar.api.ServerComponent; 030 import org.sonar.api.rules.ActiveRule; 031 import org.sonar.api.rules.Rule; 032 import org.sonar.api.rules.RuleFinder; 033 import org.sonar.api.rules.RulePriority; 034 import org.sonar.api.utils.ValidationMessages; 035 036 import javax.xml.stream.XMLInputFactory; 037 import javax.xml.stream.XMLStreamException; 038 import java.io.InputStreamReader; 039 import java.io.Reader; 040 import java.nio.charset.Charset; 041 import java.util.HashMap; 042 import java.util.Map; 043 044 /** 045 * @since 2.3 046 */ 047 public final class XMLProfileParser implements ServerComponent { 048 049 private RuleFinder ruleFinder; 050 051 public XMLProfileParser(RuleFinder ruleFinder) { 052 this.ruleFinder = ruleFinder; 053 } 054 055 public RulesProfile parseResource(ClassLoader classloader, String xmlClassPath, ValidationMessages messages) { 056 Reader reader = new InputStreamReader(classloader.getResourceAsStream(xmlClassPath), Charset.forName(CharEncoding.UTF_8)); 057 try { 058 return parse(reader, messages); 059 060 } finally { 061 IOUtils.closeQuietly(reader); 062 } 063 } 064 065 public RulesProfile parse(Reader reader, ValidationMessages messages) { 066 RulesProfile profile = RulesProfile.create(); 067 SMInputFactory inputFactory = initStax(); 068 try { 069 SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader); 070 rootC.advance(); // <profile> 071 SMInputCursor cursor = rootC.childElementCursor(); 072 while (cursor.getNext() != null) { 073 String nodeName = cursor.getLocalName(); 074 if (StringUtils.equals("rules", nodeName)) { 075 SMInputCursor rulesCursor = cursor.childElementCursor("rule"); 076 processRules(rulesCursor, profile, messages); 077 078 } else if (StringUtils.equals("name", nodeName)) { 079 profile.setName(StringUtils.trim(cursor.collectDescendantText(false))); 080 081 } else if (StringUtils.equals("language", nodeName)) { 082 profile.setLanguage(StringUtils.trim(cursor.collectDescendantText(false))); 083 } 084 } 085 } catch (XMLStreamException e) { 086 messages.addErrorText("XML is not valid: " + e.getMessage()); 087 } 088 checkProfile(profile, messages); 089 return profile; 090 } 091 092 private void checkProfile(RulesProfile profile, ValidationMessages messages) { 093 if (StringUtils.isBlank(profile.getName())) { 094 messages.addErrorText("The mandatory node <name> is missing."); 095 } 096 if (StringUtils.isBlank(profile.getLanguage())) { 097 messages.addErrorText("The mandatory node <language> is missing."); 098 } 099 } 100 101 private SMInputFactory initStax() { 102 XMLInputFactory xmlFactory = XMLInputFactory2.newInstance(); 103 xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); 104 xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE); 105 // just so it won't try to load DTD in if there's DOCTYPE 106 xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE); 107 xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE); 108 SMInputFactory inputFactory = new SMInputFactory(xmlFactory); 109 return inputFactory; 110 } 111 112 private void processRules(SMInputCursor rulesCursor, RulesProfile profile, ValidationMessages messages) throws XMLStreamException { 113 Map<String, String> parameters = new HashMap<String, String>(); 114 while (rulesCursor.getNext() != null) { 115 SMInputCursor ruleCursor = rulesCursor.childElementCursor(); 116 117 String repositoryKey = null, key = null; 118 RulePriority priority = null; 119 parameters.clear(); 120 121 while (ruleCursor.getNext() != null) { 122 String nodeName = ruleCursor.getLocalName(); 123 124 if (StringUtils.equals("repositoryKey", nodeName)) { 125 repositoryKey = StringUtils.trim(ruleCursor.collectDescendantText(false)); 126 127 } else if (StringUtils.equals("key", nodeName)) { 128 key = StringUtils.trim(ruleCursor.collectDescendantText(false)); 129 130 } else if (StringUtils.equals("priority", nodeName)) { 131 priority = RulePriority.valueOf(StringUtils.trim(ruleCursor.collectDescendantText(false))); 132 133 } else if (StringUtils.equals("parameters", nodeName)) { 134 SMInputCursor propsCursor = ruleCursor.childElementCursor("parameter"); 135 processParameters(propsCursor, parameters); 136 } 137 } 138 139 Rule rule = ruleFinder.findByKey(repositoryKey, key); 140 if (rule == null) { 141 messages.addWarningText("Rule not found: [repository=" + repositoryKey + ", key=" + key + "]"); 142 143 } else { 144 ActiveRule activeRule = profile.activateRule(rule, priority); 145 for (Map.Entry<String, String> entry : parameters.entrySet()) { 146 if (rule.getParam(entry.getKey()) == null) { 147 messages.addWarningText("The parameter '" + entry.getKey() + "' does not exist in the rule: [repository=" + repositoryKey + ", key=" + key + "]"); 148 } else { 149 activeRule.setParameter(entry.getKey(), entry.getValue()); 150 } 151 } 152 } 153 } 154 } 155 156 private void processParameters(SMInputCursor propsCursor, Map<String, String> parameters) throws XMLStreamException { 157 while (propsCursor.getNext() != null) { 158 SMInputCursor propCursor = propsCursor.childElementCursor(); 159 String key = null; 160 String value = null; 161 while (propCursor.getNext() != null) { 162 String nodeName = propCursor.getLocalName(); 163 if (StringUtils.equals("key", nodeName)) { 164 key = StringUtils.trim(propCursor.collectDescendantText(false)); 165 166 } else if (StringUtils.equals("value", nodeName)) { 167 value = StringUtils.trim(propCursor.collectDescendantText(false)); 168 } 169 } 170 if (key != null) { 171 parameters.put(key, value); 172 } 173 } 174 } 175 176 177 }