summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java')
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java209
1 files changed, 209 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java
new file mode 100644
index 000000000000..1b641a695f2e
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.java.decompiler.modules.decompiler;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConcatenationHelper {
+
+ private static final String builderClass = "java/lang/StringBuilder";
+ private static final String bufferClass = "java/lang/StringBuffer";
+ private static final String stringClass = "java/lang/String";
+
+ private static final VarType builderType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuilder");
+ private static final VarType bufferType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuffer");
+
+
+ public static Exprent contractStringConcat(Exprent expr) {
+
+ Exprent exprTmp = null;
+ VarType cltype = null;
+
+ // first quick test
+ if (expr.type == Exprent.EXPRENT_INVOCATION) {
+ InvocationExprent iex = (InvocationExprent)expr;
+ if ("toString".equals(iex.getName())) {
+ if (builderClass.equals(iex.getClassname())) {
+ cltype = builderType;
+ }
+ else if (bufferClass.equals(iex.getClassname())) {
+ cltype = bufferType;
+ }
+ if (cltype != null) {
+ exprTmp = iex.getInstance();
+ }
+ }
+ }
+
+ if (exprTmp == null) {
+ return expr;
+ }
+
+
+ // iterate in depth, collecting possible operands
+ List<Exprent> lstOperands = new ArrayList<Exprent>();
+
+ while (true) {
+
+ int found = 0;
+
+ switch (exprTmp.type) {
+ case Exprent.EXPRENT_INVOCATION:
+ InvocationExprent iex = (InvocationExprent)exprTmp;
+ if (isAppendConcat(iex, cltype)) {
+ lstOperands.add(0, iex.getLstParameters().get(0));
+ exprTmp = iex.getInstance();
+ found = 1;
+ }
+ break;
+ case Exprent.EXPRENT_NEW:
+ NewExprent nex = (NewExprent)exprTmp;
+ if (isNewConcat(nex, cltype)) {
+ VarType[] params = nex.getConstructor().getDescriptor().params;
+ if (params.length == 1) {
+ lstOperands.add(0, nex.getConstructor().getLstParameters().get(0));
+ }
+ found = 2;
+ }
+ }
+
+ if (found == 0) {
+ return expr;
+ }
+ else if (found == 2) {
+ break;
+ }
+ }
+
+ int first2str = 0;
+ int index = 0;
+ while (index < lstOperands.size() && index < 2) {
+ if (lstOperands.get(index).getExprType().equals(VarType.VARTYPE_STRING)) {
+ first2str |= (index + 1);
+ }
+ index++;
+ }
+
+ if (first2str == 0) {
+ lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, ""));
+ }
+
+ // remove redundant String.valueOf
+ for (int i = 0; i < lstOperands.size(); i++) {
+ Exprent rep = removeStringValueOf(lstOperands.get(i));
+
+ boolean ok = (i > 1);
+ if (!ok) {
+ boolean isstr = rep.getExprType().equals(VarType.VARTYPE_STRING);
+ ok = isstr || first2str != i + 1;
+
+ if (i == 0) {
+ first2str &= 2;
+ }
+ }
+
+ if (ok) {
+ lstOperands.set(i, rep);
+ }
+ }
+
+ // build exprent to return
+ Exprent func = lstOperands.get(0);
+
+ for (int i = 1; i < lstOperands.size(); i++) {
+ List<Exprent> lstTmp = new ArrayList<Exprent>();
+ lstTmp.add(func);
+ lstTmp.add(lstOperands.get(i));
+ func = new FunctionExprent(FunctionExprent.FUNCTION_STRCONCAT, lstTmp);
+ }
+
+ return func;
+ }
+
+ private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) {
+
+ if ("append".equals(expr.getName())) {
+ MethodDescriptor md = expr.getDescriptor();
+ if (md.ret.equals(cltype) && md.params.length == 1) {
+ VarType param = md.params[0];
+ switch (param.type) {
+ case CodeConstants.TYPE_OBJECT:
+ if (!param.equals(VarType.VARTYPE_STRING) &&
+ !param.equals(VarType.VARTYPE_OBJECT)) {
+ break;
+ }
+ case CodeConstants.TYPE_BOOLEAN:
+ case CodeConstants.TYPE_CHAR:
+ case CodeConstants.TYPE_DOUBLE:
+ case CodeConstants.TYPE_FLOAT:
+ case CodeConstants.TYPE_INT:
+ case CodeConstants.TYPE_LONG:
+ return true;
+ default:
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isNewConcat(NewExprent expr, VarType cltype) {
+
+ if (expr.getNewtype().equals(cltype)) {
+ VarType[] params = expr.getConstructor().getDescriptor().params;
+ if (params.length == 0 || (params.length == 1 &&
+ params[0].equals(VarType.VARTYPE_STRING))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static Exprent removeStringValueOf(Exprent exprent) {
+
+ if (exprent.type == Exprent.EXPRENT_INVOCATION) {
+ InvocationExprent iex = (InvocationExprent)exprent;
+ if ("valueOf".equals(iex.getName()) && stringClass.equals(iex.getClassname())) {
+ MethodDescriptor md = iex.getDescriptor();
+ if (md.params.length == 1) {
+ VarType param = md.params[0];
+ switch (param.type) {
+ case CodeConstants.TYPE_OBJECT:
+ if (!param.equals(VarType.VARTYPE_OBJECT)) {
+ break;
+ }
+ case CodeConstants.TYPE_BOOLEAN:
+ case CodeConstants.TYPE_CHAR:
+ case CodeConstants.TYPE_DOUBLE:
+ case CodeConstants.TYPE_FLOAT:
+ case CodeConstants.TYPE_INT:
+ case CodeConstants.TYPE_LONG:
+ return iex.getLstParameters().get(0);
+ }
+ }
+ }
+ }
+
+ return exprent;
+ }
+}