JavaIdentifierTransformer.java
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2006-2020 the original author or authors.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     https://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package org.kordamp.json.util;
019 
020 import org.apache.commons.lang3.StringUtils;
021 import org.kordamp.json.JSONException;
022 
023 /**
024  * Transforms a string into a valid Java identifier.<br>
025  * There are five predefined strategies:
026  <ul>
027  <li>NOOP: does not perform transformation.</li>
028  <li>CAMEL_CASE: follows the camel case convention, deletes non
029  * JavaIndentifierPart chars.</li>
030  <li>UNDERSCORE: transform whitespace and non JavaIdentifierPart chars to
031  * '_'.</li>
032  <li>WHITESPACE: deletes whitespace and non JavaIdentifierPart chars.</li>
033  <li>STRICT: always throws a JSONException, does not perform transformation.</li>
034  </ul>
035  *
036  @author Andres Almiray
037  */
038 public abstract class JavaIdentifierTransformer {
039     /**
040      * CamelCase transformer 'camel case' => 'camelCase'
041      */
042     public static final JavaIdentifierTransformer CAMEL_CASE = new CamelCaseJavaIdentifierTransformer();
043     /**
044      * Noop transformer '@invalid' => '@invalid'
045      */
046     public static final JavaIdentifierTransformer NOOP = new NoopJavaIdentifierTransformer();
047     /**
048      * Strict transformer '@invalid' => JSONException
049      */
050     public static final JavaIdentifierTransformer STRICT = new StrictJavaIdentifierTransformer();
051     /**
052      * Underscore transformer 'under score' => 'under_score'
053      */
054     public static final JavaIdentifierTransformer UNDERSCORE = new UnderscoreJavaIdentifierTransformer();
055     /**
056      * Whitespace transformer 'white space' => 'whitespace'
057      */
058     public static final JavaIdentifierTransformer WHITESPACE = new WhiteSpaceJavaIdentifierTransformer();
059 
060     public abstract String transformToJavaIdentifier(String str);
061 
062     /**
063      * Removes all non JavaIdentifier chars from the start of the string.
064      *
065      @throws JSONException if the resulting string has zero length.
066      */
067     protected final String shaveOffNonJavaIdentifierStartChars(String str) {
068         String str2 = str;
069         // shave off first char if not valid
070         boolean ready = false;
071         while (!ready) {
072             if (!Character.isJavaIdentifierStart(str2.charAt(0))) {
073                 str2 = str2.substring(1);
074                 if (str2.length() == 0) {
075                     throw new JSONException("Can't convert '" + str + "' to a valid Java identifier");
076                 }
077             else {
078                 ready = true;
079             }
080         }
081         return str2;
082     }
083 
084     private static final class CamelCaseJavaIdentifierTransformer extends JavaIdentifierTransformer {
085         public String transformToJavaIdentifier(String str) {
086             if (str == null) {
087                 return null;
088             }
089 
090             String str2 = shaveOffNonJavaIdentifierStartChars(str);
091 
092             char[] chars = str2.toCharArray();
093             int pos = 0;
094             StringBuffer buf = new StringBuffer();
095             boolean toUpperCaseNextChar = false;
096             while (pos < chars.length) {
097                 if (!Character.isJavaIdentifierPart(chars[pos])
098                     || Character.isWhitespace(chars[pos])) {
099                     toUpperCaseNextChar = true;
100                 else {
101                     if (toUpperCaseNextChar) {
102                         buf.append(Character.toUpperCase(chars[pos]));
103                         toUpperCaseNextChar = false;
104                     else {
105                         buf.append(chars[pos]);
106                     }
107                 }
108                 pos++;
109             }
110             return buf.toString();
111         }
112     }
113 
114     private static final class NoopJavaIdentifierTransformer extends JavaIdentifierTransformer {
115         public String transformToJavaIdentifier(String str) {
116             return str;
117         }
118     }
119 
120     private static final class StrictJavaIdentifierTransformer extends JavaIdentifierTransformer {
121         public String transformToJavaIdentifier(String str) {
122             throw new JSONException("'" + str + "' is not a valid Java identifier.");
123         }
124     }
125 
126     private static final class UnderscoreJavaIdentifierTransformer extends JavaIdentifierTransformer {
127         public String transformToJavaIdentifier(String str) {
128             if (str == null) {
129                 return null;
130             }
131             String str2 = shaveOffNonJavaIdentifierStartChars(str);
132 
133             char[] chars = str2.toCharArray();
134             int pos = 0;
135             StringBuffer buf = new StringBuffer();
136             boolean toUnderScorePreviousChar = false;
137             while (pos < chars.length) {
138                 if (!Character.isJavaIdentifierPart(chars[pos])
139                     || Character.isWhitespace(chars[pos])) {
140                     toUnderScorePreviousChar = true;
141                 else {
142                     if (toUnderScorePreviousChar) {
143                         buf.append("_");
144                         toUnderScorePreviousChar = false;
145                     }
146                     buf.append(chars[pos]);
147                 }
148                 pos++;
149             }
150             if (buf.charAt(buf.length() 1== '_') {
151                 buf.deleteCharAt(buf.length() 1);
152             }
153             return buf.toString();
154         }
155     }
156 
157     private static final class WhiteSpaceJavaIdentifierTransformer extends JavaIdentifierTransformer {
158         public String transformToJavaIdentifier(String str) {
159             if (str == null) {
160                 return null;
161             }
162             String str2 = shaveOffNonJavaIdentifierStartChars(str);
163             str2 = StringUtils.deleteWhitespace(str2);
164             char[] chars = str2.toCharArray();
165             int pos = 0;
166             StringBuffer buf = new StringBuffer();
167             while (pos < chars.length) {
168                 if (Character.isJavaIdentifierPart(chars[pos])) {
169                     buf.append(chars[pos]);
170                 }
171                 pos++;
172             }
173             return buf.toString();
174         }
175     }
176 }