diff --git a/src/main/java/net/socialgamer/cah/UpdateGoConstants.java b/src/main/java/net/socialgamer/cah/UpdateGoConstants.java new file mode 100644 index 0000000..a6d114b --- /dev/null +++ b/src/main/java/net/socialgamer/cah/UpdateGoConstants.java @@ -0,0 +1,215 @@ +/** + * Copyright (c) 2012-2018, Andy Janata + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.socialgamer.cah; + +import java.io.File; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +import net.socialgamer.cah.Constants.DoubleLocalizable; +import net.socialgamer.cah.Constants.Localizable; + + +/** + * Analyze the server enums using reflection and create a Go version for the IRC bridge to use. + * + * @author Andy Janata (ajanata@socialgamer.net) + */ +public class UpdateGoConstants { + + private static final String enumHeaderFmt = + "type %s string\r\nconst (\r\n"; + private static final String enumValueFmt = "\t%s_%s %s = \"%s\"\r\n"; + private static final String enumTailFmt = ")\r\n"; + + private static final String msgHeaderFmt = "var %sMsgs = map[string]string{\r\n"; + private static final String msgValueFmt = "\t \"%s\": \"%s\",\r\n"; + private static final String msgTailFmt = "}\r\n"; + + private static final String msg2HeaderFmt = "var %sMsgs2 = map[string]string{\r\n"; + private static final String msg2ValueFmt = "\t \"%s\": \"%s\",\r\n"; + private static final String msg2TailFmt = "}\r\n"; + + /** + * Run the enum updater. The working directory for this program should be the project's root. + * + * @param args + */ + @SuppressWarnings("rawtypes") + public static void main(final String[] args) throws Exception { + final String dir = ""; + final File outFile = new File(dir + "constants.go"); + assert outFile.canWrite(); + assert outFile.delete(); + assert outFile.createNewFile(); + final PrintWriter writer = new PrintWriter(outFile); + + writer.println("// This file is automatically generated. Do not edit."); + writer.println(); + writer.println("package pyx"); + writer.println(); + writer.println("import ()"); + writer.println(); + + final Class[] classes = Constants.class.getClasses(); + for (final Class c : classes) { + // We only care about enums. + if (!c.isEnum()) { + continue; + } + // We need to know the name of the enum itself, not that it's Constants$Foo. + final String cName = c.getName().split("\\$")[1]; + System.out.println(cName); + writer.format(enumHeaderFmt, cName); + final Map values = getEnumValues(c); + for (final String key : values.keySet()) { + final String value = values.get(key); + writer.format(enumValueFmt, cName, key, cName, value); + } + writer.println(enumTailFmt); + + if (Localizable.class.isAssignableFrom(c) || DoubleLocalizable.class.isAssignableFrom(c)) { + System.out.println(cName + "_msg"); + writer.format(msgHeaderFmt, cName); + final Map messages = getEnumMessageValues(c); + for (final String key : messages.keySet()) { + final String value = messages.get(key); + writer.format(msgValueFmt, key, value); + } + writer.println(msgTailFmt); + } + + if (DoubleLocalizable.class.isAssignableFrom(c)) { + System.out.println(cName + "_msg_2"); + writer.format(msg2HeaderFmt, cName); + final Map messages = getEnumMessage2Values(c); + for (final String key : messages.keySet()) { + final String value = messages.get(key); + writer.format(msg2ValueFmt, key, value); + } + writer.println(msg2TailFmt); + } + writer.println(); + } + writer.flush(); + writer.close(); + } + + /** + * Return a map of enum values in an Enum class, with the enum field names as keys and the values + * of toString() as the values. + * + * @param enumClass + * The Enum to examine. + * @return Map of field name -> toString values. + * @throws IllegalArgumentException + * Thrown if {@code enumClass} isn't actually an enum. + * @throws IllegalAccessException + * If the value was unable to be retrieved. + */ + private static Map getEnumValues(final Class enumClass) + throws IllegalArgumentException, IllegalAccessException { + if (!enumClass.isEnum()) { + throw new IllegalArgumentException(enumClass.getName() + " is not an enum"); + } + + final Field[] flds = enumClass.getDeclaredFields(); + final HashMap enumMap = new HashMap(); + for (final Field f : flds) { + if (f.isEnumConstant()) { + enumMap.put(f.getName(), f.get(null).toString()); + } + } + return enumMap; + } + + /** + * Return a map of {@code Localizable} message values in an Enum class, with the enum field names + * as keys and the values of getString() as the values. + * + * @param enumClass + * The Enum to examine. + * @return Map of field name -> getString values. + * @throws IllegalArgumentException + * Thrown if {@code enumClass} isn't actually an enum. + * @throws IllegalAccessException + * If the value was unable to be retrieved. + */ + private static Map getEnumMessageValues(final Class enumClass) + throws IllegalArgumentException, IllegalAccessException { + if (!enumClass.isEnum()) { + throw new IllegalArgumentException(enumClass.getName() + " is not an enum"); + } else if (!Localizable.class.isAssignableFrom(enumClass) + && !DoubleLocalizable.class.isAssignableFrom(enumClass)) { + throw new IllegalArgumentException(enumClass.getName() + + " does not implement Localizable or DoubleLocalizable."); + } + + final Field[] flds = enumClass.getDeclaredFields(); + final HashMap messageMap = new HashMap(); + for (final Field f : flds) { + if (f.isEnumConstant()) { + if (Localizable.class.isAssignableFrom(enumClass)) { + messageMap.put(f.get(null).toString(), ((Localizable) f.get(null)).getString()); + } else { + messageMap.put(f.get(null).toString(), ((DoubleLocalizable) f.get(null)).getString()); + } + } + } + return messageMap; + } + + /** + * Return a map of {@code DoubleLocalizable} message values in an Enum class, with the enum field + * names as keys and the values of getString2() as the values. + * + * @param enumClass + * The Enum to examine. + * @return Map of field name -> getString2 values. + * @throws IllegalArgumentException + * Thrown if {@code enumClass} isn't actually an enum. + * @throws IllegalAccessException + * If the value was unable to be retrieved. + */ + private static Map getEnumMessage2Values(final Class enumClass) + throws IllegalArgumentException, IllegalAccessException { + if (!enumClass.isEnum()) { + throw new IllegalArgumentException(enumClass.getName() + " is not an enum"); + } else if (!DoubleLocalizable.class.isAssignableFrom(enumClass)) { + throw new IllegalArgumentException(enumClass.getName() + + " does not implement DoubleLocalizable."); + } + + final Field[] flds = enumClass.getDeclaredFields(); + final HashMap messageMap = new HashMap(); + for (final Field f : flds) { + if (f.isEnumConstant()) { + messageMap.put(f.get(null).toString(), ((DoubleLocalizable) f.get(null)).getString2()); + } + } + return messageMap; + } +}