View Javadoc

1   /*
2    * Copyright 2011-2013 smartics, Kronseder & Reiner GmbH
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  package de.smartics.pmd.rules.strings;
17  
18  import java.nio.ByteBuffer;
19  import java.nio.CharBuffer;
20  import java.nio.charset.CharacterCodingException;
21  import java.nio.charset.Charset;
22  import java.nio.charset.CharsetDecoder;
23  
24  import net.sourceforge.pmd.AbstractJavaRule;
25  import net.sourceforge.pmd.PropertyDescriptor;
26  import net.sourceforge.pmd.ast.ASTLiteral;
27  import net.sourceforge.pmd.properties.StringProperty;
28  
29  /**
30   * Rule to detect usage of characters of disallowed encoding in String literals.
31   */
32  public class StringLiteralEncodingRule extends AbstractJavaRule
33  {
34    // ********************************* Fields *********************************
35  
36    // --- constants ------------------------------------------------------------
37  
38    /**
39     * The descriptor to the {@link #encoding} property of this rule.
40     */
41    private static final PropertyDescriptor ENCODING_DESCRIPTOR =
42        new StringProperty("encoding",
43            "The name of the allowed encoding for string literals.", "US-ASCII",
44            1.0f);
45  
46    // --- members --------------------------------------------------------------
47  
48    /**
49     * The allowed encoding.
50     */
51    private String encoding;
52  
53    /**
54     * The decoder dependent on the {@link #encoding}.
55     */
56    private CharsetDecoder decoder;
57  
58    // ****************************** Initializer *******************************
59  
60    // ****************************** Constructors ******************************
61  
62    // ****************************** Inner Classes *****************************
63  
64    // ********************************* Methods ********************************
65  
66    // --- init -----------------------------------------------------------------
67  
68    // --- get&set --------------------------------------------------------------
69  
70    // --- business -------------------------------------------------------------
71  
72    /**
73     * Checks that there are disallowed encoded characters in String literals.
74     * {@inheritDoc}
75     *
76     * @see net.sourceforge.pmd.AbstractJavaRule#visit(net.sourceforge.pmd.ast.ASTLiteral,
77     *      java.lang.Object)
78     */
79    @Override
80    public Object visit(final ASTLiteral node, final Object data)
81    {
82      provideRuleProperties();
83  
84      if (node.isStringLiteral())
85      {
86        final String literal = node.getImage();
87        if (!isProperlyEncoded(literal))
88        {
89          final String message = createMessage(literal);
90          addViolationWithMessage(data, node, message);
91        }
92      }
93  
94      return super.visit(node, data);
95    }
96  
97    private String createMessage(final String literal)
98    {
99      return "String literal '" + literal + "' is not encoded with '" + encoding
100            + "'.";
101   }
102 
103   private void provideRuleProperties()
104   {
105     if (encoding == null)
106     {
107       encoding = getStringProperty(ENCODING_DESCRIPTOR);
108       decoder = Charset.forName(encoding).newDecoder();
109     }
110   }
111 
112   /**
113    * Inspired by <a href=
114    * "http://stackoverflow.com/questions/3585053/in-java-is-it-possible-to-check-if-a-string-is-only-ascii"
115    * >In Java, is it possible to check if a String is only ASCII?</a>.
116    */
117   private boolean isProperlyEncoded(final String string)
118   {
119     final byte[] stringBytes = string.getBytes();
120 
121     try
122     {
123       final CharBuffer buffer = decoder.decode(ByteBuffer.wrap(stringBytes));
124       buffer.toString();
125     }
126     catch (final CharacterCodingException e)
127     {
128       return false;
129     }
130 
131     return true;
132   }
133 
134   // --- object basics --------------------------------------------------------
135 
136   /**
137    * Returns the string representation of the class for debugging.
138    *
139    * @return the string representation of the class for debugging.
140    */
141   @Override
142   public String toString()
143   {
144     return "StringLiteralEncodingRule with encoding '" + encoding + "'.";
145   }
146 }