1 /*
   2  * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.lang.reflect.Type;
  25 import java.util.Random;
  26 
  27 public class ValueTypeGenerator {
  28 
  29     static class FieldDescriptor {
  30 
  31         final public String name;
  32         final public Type   type;
  33         final public String typeName;
  34 
  35         public FieldDescriptor(String name, Type type) {
  36             this.name = name;
  37             this.type = type;
  38             String s = type.getTypeName();
  39             if (s.startsWith("class")) {
  40                 s = s.substring(s.lastIndexOf(".")+1);
  41             }
  42             this.typeName = s;
  43         }
  44     }
  45 
  46     static Type[] typeArray;
  47     static String[] defaultArray;
  48     static int NB_TYPES = 9;
  49 
  50     static {
  51         typeArray = new Type[NB_TYPES];
  52         typeArray[0] = byte.class;
  53         typeArray[1] = short.class;
  54         typeArray[2] = int.class;
  55         typeArray[3] = long.class;
  56         typeArray[4] = char.class;
  57         typeArray[5] = float.class;
  58         typeArray[6] = double.class;
  59         typeArray[7] = boolean.class;
  60         typeArray[8] = Object.class;
  61     }
  62 
  63     static String defaultValue(Type t) {
  64         switch(t.getTypeName()) {
  65         case "byte": return "(byte)123";
  66         case "short": return "(short)32056";
  67         case "int": return "483647";
  68         case "long": return "922337203685477L";
  69         case "char": return "(char)65456";
  70         case "float": return "2.71828f";
  71         case "double": return "3.14159d";
  72         case "boolean": return "true";
  73         case "java.lang.Object": return "new String(\"foo\")";
  74         default:
  75             throw new RuntimeException();
  76         }
  77     }
  78     static private String generateValueTypeInternal(Random random, String name, int nfields, int typeLimit) {
  79         // generate the fields
  80         FieldDescriptor[] fieldDescArray = new FieldDescriptor[nfields];
  81         for (int i = 0; i < nfields; i++) {
  82             int idx = random.nextInt(typeLimit);
  83             String s =  typeArray[idx].getTypeName();
  84             if (s.contains(".")) {
  85                 s = s.substring(s.lastIndexOf(".")+1);
  86             }
  87             String fieldName = s+"Field"+i;
  88             fieldDescArray[i] = new FieldDescriptor(fieldName, typeArray[idx]);
  89         }
  90 
  91         String source = generateSource(name, fieldDescArray);
  92         return source;
  93     }
  94 
  95     static public String generateValueType(Random random, String name, int nfields) {
  96         return generateValueTypeInternal(random, name, nfields, NB_TYPES);
  97     }
  98 
  99     static public String generateValueTypeNoObjectRef(Random random, String name, int nfields) {
 100         return generateValueTypeInternal(random, name, nfields, NB_TYPES - 1);
 101     }
 102 
 103     static String fieldsAsArgs(FieldDescriptor[] fields) {
 104         StringBuilder sb = new StringBuilder();
 105         for (int i = 0; i < fields.length; i++) {
 106             sb.append(fields[i].typeName).append(" ").append(fields[i].name);
 107             if (i != fields.length - 1) {
 108                 sb.append(", ");
 109             }
 110         }
 111         return sb.toString();
 112     }
 113 
 114     static String generateSource(String name, FieldDescriptor[] fields) {
 115         StringBuilder sb = new StringBuilder();
 116 
 117         // imports
 118         sb.append("import java.io.PrintStream;\n\n");
 119 
 120         // class declaration
 121         sb.append("public value final class ").append(name).append(" {\n");
 122 
 123         // field declarations
 124         for (FieldDescriptor f : fields) {
 125             sb.append("\tfinal public ").append(f.typeName).append(" ");
 126             sb.append(f.name).append(";\n");
 127         }
 128         sb.append("\n");
 129 
 130         // private constructor
 131         sb.append("\tprivate ").append(name).append("() {\n");
 132         for (int i = 0 ; i < fields.length; i++) {
 133             sb.append("\t\t").append(fields[i].name).append(" = ").append(defaultValue(fields[i].type)).append(";\n");
 134         }
 135         sb.append("\t}\n");
 136         sb.append("\n");
 137 
 138         // factory
 139         sb.append("\tstatic public ").append(name).append(" ").append("make").append(name).append("(");
 140         sb.append(fieldsAsArgs(fields));
 141         sb.append(") {\n");
 142         sb.append("\t\t").append(name).append(" v = ").append(name).append(".default;\n");
 143         for (int i = 0 ; i < fields.length; i++) {
 144             sb.append("\t\tv = __WithField(v.").append(fields[i].name).append(", ").append(fields[i].name).append(");\n");
 145         }
 146         sb.append("\t\treturn v;\n");
 147         sb.append("\t};\n");
 148         sb.append("\n");
 149 
 150         // default factory
 151         sb.append("\tstatic public ").append(name).append(" ").append("make").append(name).append("() {\n");
 152         sb.append("\t\t").append(name).append(" v = ").append(name).append(".default;\n");
 153         for (int i = 0 ; i < fields.length; i++) {
 154             sb.append("\t\tv = __WithField(v.").append(fields[i].name).append(", ").append(defaultValue(fields[i].type)).append(");\n");
 155         }
 156         sb.append("\t\treturn v;\n");
 157         sb.append("\t}\n");
 158         sb.append("\n");
 159 
 160         // verify method
 161         sb.append("\tstatic public boolean verify(").append(name).append(" value) {\n");
 162         for (FieldDescriptor f : fields) {
 163             if (f.type.getTypeName().compareTo("java.lang.Object") == 0) {
 164                 sb.append("\t\tif (((String)value.").append(f.name).append(").compareTo(").append(defaultValue(f.type)).append(") != 0) return false;\n");
 165             } else {
 166                 sb.append("\t\tif (value.").append(f.name).append(" != ").append(defaultValue(f.type)).append(") return false;\n");
 167             }
 168         }
 169         sb.append("\t\treturn true;\n");
 170         sb.append("\t}\n");
 171 
 172         // printLayout method
 173         sb.append("\tstatic public void printLayout(PrintStream out) {\n");
 174         sb.append("\t\tout.println(\"").append(name).append(" fields: ");
 175         for (int i = 0; i < fields.length; i++) {
 176             sb.append(fields[i].typeName);
 177             if (i != fields.length - 1) {
 178                 sb.append(", ");
 179             }
 180         }
 181         sb.append("\");\n");
 182         sb.append("\t}\n");
 183 
 184         sb.append("}\n");
 185 
 186         return sb.toString();
 187     }
 188 }