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 }