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.rules;
021
022 import com.thoughtworks.xstream.XStream;
023 import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
024 import com.thoughtworks.xstream.core.util.QuickWriter;
025 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
026 import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
027 import com.thoughtworks.xstream.io.xml.XppDriver;
028 import org.apache.commons.io.IOUtils;
029 import org.apache.commons.lang.CharEncoding;
030 import org.apache.commons.lang.StringUtils;
031 import org.sonar.api.utils.SonarException;
032
033 import java.io.*;
034 import java.lang.ref.WeakReference;
035 import java.util.*;
036
037 public class StandardRulesXmlParser {
038
039 /**
040 * see the XML format into the unit test src/test/java/.../StandardRulesXmlParserTest
041 */
042 public List<Rule> parse(String xml) {
043 InputStream inputStream = null;
044 try {
045 inputStream = IOUtils.toInputStream(xml, CharEncoding.UTF_8);
046 return setDefaultRulePriorities((List<Rule>) getXStream().fromXML(inputStream));
047
048 } catch (IOException e) {
049 throw new SonarException("Can't parse xml file", e);
050
051 } finally {
052 IOUtils.closeQuietly(inputStream);
053 }
054 }
055
056 public List<Rule> parse(Reader reader) {
057 return setDefaultRulePriorities((List<Rule>) getXStream().fromXML(reader));
058 }
059
060 public List<Rule> parse(InputStream input) {
061 try {
062 return setDefaultRulePriorities((List<Rule>) getXStream().fromXML(new InputStreamReader(input, CharEncoding.UTF_8)));
063
064 } catch (UnsupportedEncodingException e) {
065 throw new SonarException("Can't parse xml file", e);
066 }
067 }
068
069 private List<Rule> setDefaultRulePriorities(List<Rule> rules) {
070 for (Rule rule : rules) {
071 if (rule.getPriority() == null) {
072 rule.setPriority(RulePriority.MAJOR);
073 }
074 }
075 return rules;
076 }
077
078 public String toXml(List<Rule> rules) {
079 return getXStream().toXML(rules);
080 }
081
082 private static class CDataXppDriver extends XppDriver {
083 @Override
084 public HierarchicalStreamWriter createWriter(Writer out) {
085 return new XDataPrintWriter(out);
086 }
087 }
088
089 private static class XDataPrintWriter extends PrettyPrintWriter {
090 public XDataPrintWriter(Writer writer) {
091 super(writer);
092 }
093
094 @Override
095 protected void writeText(QuickWriter writer, String text) {
096 writer.write("<![CDATA[");
097 writer.write(text);
098 writer.write("]]>");
099 }
100 }
101
102 private XStream getXStream() {
103 XStream xstream = new XStream(new CDataXppDriver());
104 xstream.registerConverter(new TrimStringConverter());
105 xstream.alias("rules", ArrayList.class);
106
107 xstream.alias("categ", RulesCategory.class);
108 xstream.useAttributeFor(RulesCategory.class, "name");
109 xstream.aliasField("category", Rule.class, "rulesCategory");
110
111 xstream.alias("rule", Rule.class);
112 xstream.useAttributeFor(Rule.class, "key");
113 xstream.useAttributeFor("priority", RulePriority.class);
114
115 xstream.addImplicitCollection(Rule.class, "params");
116
117 xstream.alias("param", RuleParam.class);
118 xstream.useAttributeFor(RuleParam.class, "key");
119 xstream.useAttributeFor(RuleParam.class, "type");
120
121 // only for backward compatibility with sonar 1.4.
122 xstream.omitField(RuleParam.class, "defaultValue");
123 return xstream;
124 }
125
126 /**
127 * See http://svn.codehaus.org/xstream/trunk/xstream/src/java/com/thoughtworks/xstream/converters/basic/StringConverter.java
128 */
129 public static class TrimStringConverter extends AbstractSingleValueConverter {
130
131 private final Map cache;
132
133 public TrimStringConverter(final Map map) {
134 cache = map;
135 }
136
137 public TrimStringConverter() {
138 this(Collections.synchronizedMap(new WeakHashMap()));
139 }
140
141 public boolean canConvert(final Class type) {
142 return type.equals(String.class);
143 }
144
145 public Object fromString(final String str) {
146 String trim = StringUtils.trim(str);
147 final WeakReference ref = (WeakReference) cache.get(trim);
148 String s = (String) (ref == null ? null : ref.get());
149
150 if (s == null) {
151 // fill cache
152 cache.put(str, new WeakReference(trim));
153 s = trim;
154 }
155
156 return s;
157 }
158 }
159 }