diff --git a/plugin.yml b/plugin.yml index 3dc39741..d6a92fde 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,7 +1,4 @@ name: mcore2 version: 1.0.0 main: com.massivecraft.mcore2.MCore -authors: [Olof Larsson, Brett Flannigan] -commands: - mcoresilenteater: - description: ignore me. \ No newline at end of file +authors: [Olof Larsson, Brett Flannigan] \ No newline at end of file diff --git a/src/com/massivecraft/mcore2/InternalListener.java b/src/com/massivecraft/mcore2/InternalListener.java index 11718f39..28e5d117 100644 --- a/src/com/massivecraft/mcore2/InternalListener.java +++ b/src/com/massivecraft/mcore2/InternalListener.java @@ -5,9 +5,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerPreLoginEvent; -import org.bukkit.event.server.ServerCommandEvent; import com.massivecraft.mcore2.persist.IClassManager; import com.massivecraft.mcore2.persist.Persist; @@ -23,6 +21,7 @@ public class InternalListener implements Listener Bukkit.getServer().getPluginManager().registerEvents(this, this.p); } + // TODO: Does this even trigger? If not we have an issue. @EventHandler(priority = EventPriority.LOWEST) public void onPlayerPreLogin(PlayerPreLoginEvent event) { @@ -40,26 +39,4 @@ public class InternalListener implements Listener } } } - - @EventHandler(priority = EventPriority.LOW) - public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) - { - if (event.isCancelled()) return; - if (MCore.handleCommand(event.getPlayer(), event.getMessage().substring(1), false)) - { - Bukkit.getLogger().info("[PLAYER_COMMAND] "+event.getPlayer().getName()+": "+event.getMessage()); - event.setCancelled(true); - } - } - - private final static String refCommand = "mcoresilenteater"; - @EventHandler(priority = EventPriority.LOWEST) - public void onServerCommand(ServerCommandEvent event) - { - if (event.getCommand().length() == 0) return; - if (MCore.handleCommand(event.getSender(), event.getCommand(), false)) - { - event.setCommand(refCommand); - } - } } diff --git a/src/com/massivecraft/mcore2/MCore.java b/src/com/massivecraft/mcore2/MCore.java index e200757e..9161bea0 100644 --- a/src/com/massivecraft/mcore2/MCore.java +++ b/src/com/massivecraft/mcore2/MCore.java @@ -1,16 +1,12 @@ package com.massivecraft.mcore2; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; -import org.bukkit.command.CommandSender; import org.bukkit.plugin.java.JavaPlugin; import com.massivecraft.mcore2.cmd.Cmd; @@ -50,18 +46,6 @@ public class MCore extends JavaPlugin if (cmdInstances.containsKey(owner)) return; cmdInstances.put(owner, new Cmd()); } - public static boolean handleCommand(CommandSender sender, String commandString, boolean testOnly) - { - List args = new ArrayList(Arrays.asList(commandString.split("\\s+"))); - if (args.size() == 0) return false; - String alias = args.get(0); - args.remove(0); - for (Cmd cmd : cmdInstances.values()) - { - if (cmd.handleCommand(sender, alias, args, testOnly)) return true; - } - return false; - } // -------------------------------------------- // // ONE @@ -125,21 +109,6 @@ public class MCore extends JavaPlugin .excludeFieldsWithModifiers(Modifier.TRANSIENT); } - // -------------------------------------------- // - // SPOUT INTEGRATION - // -------------------------------------------- // - /*protected boolean spoutIsIntegrated = false; - protected void integrateSpout() - { - if (spoutIsIntegrated) return; - if ( ! Bukkit.getPluginManager().isPluginEnabled("Spout")) return; - - // Ok we should be safe :) Lets integrate! - this.spoutIsIntegrated = true; - - - }*/ - // -------------------------------------------- // // LOGGING // -------------------------------------------- // diff --git a/src/com/massivecraft/mcore2/cmd/BukkitGlueCommand.java b/src/com/massivecraft/mcore2/cmd/BukkitGlueCommand.java new file mode 100644 index 00000000..f7c49c06 --- /dev/null +++ b/src/com/massivecraft/mcore2/cmd/BukkitGlueCommand.java @@ -0,0 +1,31 @@ +package com.massivecraft.mcore2.cmd; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +public class BukkitGlueCommand extends Command +{ + protected MCommand mcommand; + public BukkitGlueCommand(MCommand mcommand) + { + super(mcommand.getAliases().get(0), mcommand.getDesc(), mcommand.getUseageTemplate(), mcommand.getAliases()); + this.mcommand = mcommand; + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) + { + if ( ! mcommand.p().isEnabled()) + { + return false; + } + + List argList = new ArrayList(Arrays.asList(args)); + this.mcommand.execute(sender, argList); + return true; + } +} diff --git a/src/com/massivecraft/mcore2/cmd/Cmd.java b/src/com/massivecraft/mcore2/cmd/Cmd.java index efcf4127..039d30e3 100644 --- a/src/com/massivecraft/mcore2/cmd/Cmd.java +++ b/src/com/massivecraft/mcore2/cmd/Cmd.java @@ -2,17 +2,17 @@ package com.massivecraft.mcore2.cmd; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Set; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.command.CommandSender; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.craftbukkit.CraftServer; import org.bukkit.entity.Player; import com.massivecraft.mcore2.cmd.arg.AHBoolean; +import com.massivecraft.mcore2.cmd.arg.AHByte; import com.massivecraft.mcore2.cmd.arg.AHDate; import com.massivecraft.mcore2.cmd.arg.AHDouble; import com.massivecraft.mcore2.cmd.arg.AHFloat; @@ -30,30 +30,19 @@ public class Cmd public IArgHandler getArgHandler(Class clazz) { return (IArgHandler) this.argHandlers.get(clazz); } public void setArgHandler(Class clazz, IArgHandler handler) { this.argHandlers.put(clazz, handler); } - protected Set commands = new HashSet(); - public Set getCommands() { return this.commands; } - public void addCommand(MCommand mcommand) { this.commands.add(mcommand); } - public MCommand getCommand(String alias) + /** + * @deprecated As of MCore 3, replaced by by {@link MCommand#register()} + */ + @Deprecated + public void addCommand(MCommand mcommand) { - for (MCommand command : this.commands) - { - if (command.aliases.contains(alias)) return command; - } - return null; - } - - public boolean handleCommand(CommandSender sender, String alias, List args, boolean testOnly) - { - MCommand mcommand = this.getCommand(alias); - if (mcommand == null) return false; - if (testOnly) return true; - mcommand.execute(sender, args); - return true; + mcommand.register(); } public Cmd() { this.setArgHandler(Boolean.class, new AHBoolean()); + this.setArgHandler(Byte.class, new AHByte()); this.setArgHandler(Double.class, new AHDouble()); this.setArgHandler(Date.class, new AHDate()); this.setArgHandler(Float.class, new AHFloat()); @@ -62,4 +51,10 @@ public class Cmd this.setArgHandler(Player.class, new AHPlayer()); this.setArgHandler(World.class, new AHWorld()); } + + public static SimpleCommandMap getBukkitCommandMap() + { + CraftServer craftServer = (CraftServer)Bukkit.getServer(); + return craftServer.getCommandMap(); + } } diff --git a/src/com/massivecraft/mcore2/cmd/MCommand.java b/src/com/massivecraft/mcore2/cmd/MCommand.java index d2ad7b2c..b78b58d4 100644 --- a/src/com/massivecraft/mcore2/cmd/MCommand.java +++ b/src/com/massivecraft/mcore2/cmd/MCommand.java @@ -142,35 +142,20 @@ public abstract class MCommand public Player me; public boolean senderIsConsole; - /* - public boolean getSenderIsConsole() { return ! (this.sender instanceof Player); } - public Player me() - { - if (sender instanceof Player) - { - return (Player) sender; - } - return null; - } - */ + // -------------------------------------------- // + // BUKKIT INTEGRATION + // -------------------------------------------- // - /* - @SuppressWarnings("unchecked") - public T getSenderAs(Class clazz) + public boolean register() { - if (clazz.isInstance(sender)) return (T) sender; - - for (Persist realm : MCore.getPersistInstances().values()) - { - for (IClassManager manager : realm.getClassManagers().values()) - { - if ( ! manager.getManagedClass().equals(clazz)) continue; - if (manager.idCanFix(sender.getClass()) == false) continue; - return (T) manager.get(sender); - } - } - return null; - }*/ + // TODO: Save this somewhere? And update it on changes to the aliases? + BukkitGlueCommand bgc = new BukkitGlueCommand(this); + return Cmd.getBukkitCommandMap().register("mcore", bgc); + } + + // -------------------------------------------- // + // CONSTRUCTORS AND EXECUTOR + // -------------------------------------------- // public MCommand() { diff --git a/src/com/massivecraft/mcore2/cmd/arg/AHByte.java b/src/com/massivecraft/mcore2/cmd/arg/AHByte.java new file mode 100644 index 00000000..e4869938 --- /dev/null +++ b/src/com/massivecraft/mcore2/cmd/arg/AHByte.java @@ -0,0 +1,16 @@ +package com.massivecraft.mcore2.cmd.arg; + +public class AHByte extends AHPrimitive +{ + @Override + protected String getPrimitiveName() + { + return "byte"; + } + + @Override + protected Byte unsafeConvert(String str) throws Exception + { + return Byte.parseByte(str); + } +} diff --git a/src/com/massivecraft/mcore2/lib/gson/AnonymousAndLocalClassExclusionStrategy.java b/src/com/massivecraft/mcore2/lib/gson/AnonymousAndLocalClassExclusionStrategy.java deleted file mode 100644 index 00616b32..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/AnonymousAndLocalClassExclusionStrategy.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -/** - * Strategy for excluding anonymous and local classes. - * - * @author Joel Leitch - */ -final class AnonymousAndLocalClassExclusionStrategy implements ExclusionStrategy { - - public boolean shouldSkipField(FieldAttributes f) { - return isAnonymousOrLocal(f.getDeclaredClass()); - } - - public boolean shouldSkipClass(Class clazz) { - return isAnonymousOrLocal(clazz); - } - - private boolean isAnonymousOrLocal(Class clazz) { - return !Enum.class.isAssignableFrom(clazz) - && (clazz.isAnonymousClass() || clazz.isLocalClass()); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/Cache.java b/src/com/massivecraft/mcore2/lib/gson/Cache.java deleted file mode 100644 index 7190f7b7..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/Cache.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -/** - * Defines generic cache interface. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -interface Cache { - - /** - * Adds the new value object into the cache for the given key. If the key already - * exists, then this method will override the value for the key. - * - * @param key the key identifier for the {@code value} object - * @param value the value object to store in the cache - */ - void addElement(K key, V value); - - /** - * Retrieve the cached value for the given {@code key}. - * - * @param key the key identifying the value - * @return the cached value for the given {@code key} - */ - V getElement(K key); -} diff --git a/src/com/massivecraft/mcore2/lib/gson/CamelCaseSeparatorNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/CamelCaseSeparatorNamingPolicy.java deleted file mode 100644 index 83701db0..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/CamelCaseSeparatorNamingPolicy.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collection; - -/** - * Converts the field name that uses camel-case define word separation into separate words that - * are separated by the provided {@code separatorString}. - * - *

The following is an example:

- *
- * class IntWrapper {
- *   public int integerField = 0;
- * }
- *
- * CamelCaseSeparatorNamingPolicy policy = new CamelCaseSeparatorNamingPolicy("_");
- * String translatedFieldName =
- *     policy.translateName(IntWrapper.class.getField("integerField"));
- *
- * assert("integer_Field".equals(translatedFieldName));
- * 
- * - * @author Joel Leitch - */ -final class CamelCaseSeparatorNamingPolicy extends RecursiveFieldNamingPolicy { - private final String separatorString; - - /** - * Constructs a new CamelCaseSeparatorNamingPolicy object that will add the - * {@code separatorString} between each of the words separated by camel case. - * - * @param separatorString the string value to place between words - * @throws IllegalArgumentException thrown if the {@code separatorString} parameter - * is null or empty. - */ - public CamelCaseSeparatorNamingPolicy(String separatorString) { - $Gson$Preconditions.checkNotNull(separatorString); - $Gson$Preconditions.checkArgument(!"".equals(separatorString)); - this.separatorString = separatorString; - } - - @Override - protected String translateName(String target, Type fieldType, - Collection annnotations) { - StringBuilder translation = new StringBuilder(); - for (int i = 0; i < target.length(); i++) { - char character = target.charAt(i); - if (Character.isUpperCase(character) && translation.length() != 0) { - translation.append(separatorString); - } - translation.append(character); - } - - return translation.toString(); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/CompositionFieldNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/CompositionFieldNamingPolicy.java deleted file mode 100644 index 949e1354..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/CompositionFieldNamingPolicy.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collection; - -/** - * Performs numerous field naming translations wrapped up as one object. - * - * @author Joel Leitch - */ -abstract class CompositionFieldNamingPolicy extends RecursiveFieldNamingPolicy { - - private final RecursiveFieldNamingPolicy[] fieldPolicies; - - public CompositionFieldNamingPolicy(RecursiveFieldNamingPolicy... fieldNamingPolicies) { - if (fieldNamingPolicies == null) { - throw new NullPointerException("naming policies can not be null."); - } - this.fieldPolicies = fieldNamingPolicies; - } - - @Override - protected String translateName(String target, Type fieldType, Collection annotations) { - for (RecursiveFieldNamingPolicy policy : fieldPolicies) { - target = policy.translateName(target, fieldType, annotations); - } - return target; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/DefaultDateTypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/DefaultDateTypeAdapter.java new file mode 100644 index 00000000..d1dd2702 --- /dev/null +++ b/src/com/massivecraft/mcore2/lib/gson/DefaultDateTypeAdapter.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * 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 com.massivecraft.mcore2.lib.gson; + +import java.lang.reflect.Type; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import com.massivecraft.mcore2.lib.gson.DefaultDateTypeAdapter; +import com.massivecraft.mcore2.lib.gson.JsonDeserializationContext; +import com.massivecraft.mcore2.lib.gson.JsonDeserializer; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonParseException; +import com.massivecraft.mcore2.lib.gson.JsonPrimitive; +import com.massivecraft.mcore2.lib.gson.JsonSerializationContext; +import com.massivecraft.mcore2.lib.gson.JsonSerializer; +import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; + +/** + * This type adapter supports three subclasses of date: Date, Timestamp, and + * java.sql.Date. + * + * @author Inderjeet Singh + * @author Joel Leitch + */ +final class DefaultDateTypeAdapter implements JsonSerializer, JsonDeserializer { + + // TODO: migrate to streaming adapter + + private final DateFormat enUsFormat; + private final DateFormat localFormat; + private final DateFormat iso8601Format; + + DefaultDateTypeAdapter() { + this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US), + DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)); + } + + DefaultDateTypeAdapter(String datePattern) { + this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern)); + } + + DefaultDateTypeAdapter(int style) { + this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style)); + } + + public DefaultDateTypeAdapter(int dateStyle, int timeStyle) { + this(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US), + DateFormat.getDateTimeInstance(dateStyle, timeStyle)); + } + + DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) { + this.enUsFormat = enUsFormat; + this.localFormat = localFormat; + this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); + this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + // These methods need to be synchronized since JDK DateFormat classes are not thread-safe + // See issue 162 + public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { + synchronized (localFormat) { + String dateFormatAsString = enUsFormat.format(src); + return new JsonPrimitive(dateFormatAsString); + } + } + + public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + if (!(json instanceof JsonPrimitive)) { + throw new JsonParseException("The date should be a string value"); + } + Date date = deserializeToDate(json); + if (typeOfT == Date.class) { + return date; + } else if (typeOfT == Timestamp.class) { + return new Timestamp(date.getTime()); + } else if (typeOfT == java.sql.Date.class) { + return new java.sql.Date(date.getTime()); + } else { + throw new IllegalArgumentException(getClass() + " cannot deserialize to " + typeOfT); + } + } + + private Date deserializeToDate(JsonElement json) { + synchronized (localFormat) { + try { + return localFormat.parse(json.getAsString()); + } catch (ParseException ignored) { + } + try { + return enUsFormat.parse(json.getAsString()); + } catch (ParseException ignored) { + } + try { + return iso8601Format.parse(json.getAsString()); + } catch (ParseException e) { + throw new JsonSyntaxException(json.getAsString(), e); + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(DefaultDateTypeAdapter.class.getSimpleName()); + sb.append('(').append(localFormat.getClass().getSimpleName()).append(')'); + return sb.toString(); + } +} diff --git a/src/com/massivecraft/mcore2/lib/gson/DefaultTypeAdapters.java b/src/com/massivecraft/mcore2/lib/gson/DefaultTypeAdapters.java deleted file mode 100644 index 94c09ce9..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/DefaultTypeAdapters.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import java.lang.reflect.Type; -import java.sql.Timestamp; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -/** - * List of all the default type adapters ({@link JsonSerializer}s, {@link JsonDeserializer}s, - * and {@link InstanceCreator}s. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class DefaultTypeAdapters { - /** - * This type adapter supports three subclasses of date: Date, Timestamp, and - * java.sql.Date. - */ - static final class DefaultDateTypeAdapter implements JsonSerializer, JsonDeserializer { - private final DateFormat enUsFormat; - private final DateFormat localFormat; - private final DateFormat iso8601Format; - - DefaultDateTypeAdapter() { - this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US), - DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)); - } - - DefaultDateTypeAdapter(String datePattern) { - this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern)); - } - - DefaultDateTypeAdapter(int style) { - this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style)); - } - - public DefaultDateTypeAdapter(int dateStyle, int timeStyle) { - this(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US), - DateFormat.getDateTimeInstance(dateStyle, timeStyle)); - } - - DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) { - this.enUsFormat = enUsFormat; - this.localFormat = localFormat; - this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); - this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - // These methods need to be synchronized since JDK DateFormat classes are not thread-safe - // See issue 162 - public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { - synchronized (localFormat) { - String dateFormatAsString = enUsFormat.format(src); - return new JsonPrimitive(dateFormatAsString); - } - } - - public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - if (!(json instanceof JsonPrimitive)) { - throw new JsonParseException("The date should be a string value"); - } - Date date = deserializeToDate(json); - if (typeOfT == Date.class) { - return date; - } else if (typeOfT == Timestamp.class) { - return new Timestamp(date.getTime()); - } else if (typeOfT == java.sql.Date.class) { - return new java.sql.Date(date.getTime()); - } else { - throw new IllegalArgumentException(getClass() + " cannot deserialize to " + typeOfT); - } - } - - private Date deserializeToDate(JsonElement json) { - synchronized (localFormat) { - try { - return localFormat.parse(json.getAsString()); - } catch (ParseException ignored) { - } - try { - return enUsFormat.parse(json.getAsString()); - } catch (ParseException ignored) { - } - try { - return iso8601Format.parse(json.getAsString()); - } catch (ParseException e) { - throw new JsonSyntaxException(json.getAsString(), e); - } - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(DefaultDateTypeAdapter.class.getSimpleName()); - sb.append('(').append(localFormat.getClass().getSimpleName()).append(')'); - return sb.toString(); - } - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/DisjunctionExclusionStrategy.java b/src/com/massivecraft/mcore2/lib/gson/DisjunctionExclusionStrategy.java deleted file mode 100644 index 94ca8ba1..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/DisjunctionExclusionStrategy.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; - -import java.util.Collection; - -/** - * A wrapper class used to collect numerous {@link ExclusionStrategy} objects - * and perform a short-circuited OR operation. - * - * @author Joel Leitch - */ -final class DisjunctionExclusionStrategy implements ExclusionStrategy { - private final Collection strategies; - - DisjunctionExclusionStrategy(Collection strategies) { - this.strategies = $Gson$Preconditions.checkNotNull(strategies); - } - - public boolean shouldSkipField(FieldAttributes f) { - for (ExclusionStrategy strategy : strategies) { - if (strategy.shouldSkipField(f)) { - return true; - } - } - return false; - } - - public boolean shouldSkipClass(Class clazz) { - for (ExclusionStrategy strategy : strategies) { - if (strategy.shouldSkipClass(clazz)) { - return true; - } - } - return false; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/ExclusionStrategy.java b/src/com/massivecraft/mcore2/lib/gson/ExclusionStrategy.java index 592c1b56..efbdd55d 100644 --- a/src/com/massivecraft/mcore2/lib/gson/ExclusionStrategy.java +++ b/src/com/massivecraft/mcore2/lib/gson/ExclusionStrategy.java @@ -16,6 +16,10 @@ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.ExclusionStrategy; +import com.massivecraft.mcore2.lib.gson.FieldAttributes; +import com.massivecraft.mcore2.lib.gson.GsonBuilder; + /** * A strategy (or policy) definition that is used to decide whether or not a field or top-level * class should be serialized or deserialized as part of the JSON output/input. For serialization, diff --git a/src/com/massivecraft/mcore2/lib/gson/ExposeAnnotationDeserializationExclusionStrategy.java b/src/com/massivecraft/mcore2/lib/gson/ExposeAnnotationDeserializationExclusionStrategy.java deleted file mode 100644 index c87aca99..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/ExposeAnnotationDeserializationExclusionStrategy.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.annotations.Expose; - -/** - * Excludes fields that do not have the {@link Expose} annotation - * - * @author Joel Leitch - */ -final class ExposeAnnotationDeserializationExclusionStrategy implements ExclusionStrategy { - public boolean shouldSkipClass(Class clazz) { - return false; - } - - public boolean shouldSkipField(FieldAttributes f) { - Expose annotation = f.getAnnotation(Expose.class); - if (annotation == null) { - return true; - } - return !annotation.deserialize(); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/ExposeAnnotationSerializationExclusionStrategy.java b/src/com/massivecraft/mcore2/lib/gson/ExposeAnnotationSerializationExclusionStrategy.java deleted file mode 100644 index 768e042d..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/ExposeAnnotationSerializationExclusionStrategy.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.annotations.Expose; - -/** - * Excludes fields that do not have the {@link Expose} annotation - * - * @author Joel Leitch - */ -final class ExposeAnnotationSerializationExclusionStrategy implements ExclusionStrategy { - public boolean shouldSkipClass(Class clazz) { - return false; - } - - public boolean shouldSkipField(FieldAttributes f) { - Expose annotation = f.getAnnotation(Expose.class); - if (annotation == null) { - return true; - } - return !annotation.serialize(); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/FieldAttributes.java b/src/com/massivecraft/mcore2/lib/gson/FieldAttributes.java index e928a5cb..837507db 100644 --- a/src/com/massivecraft/mcore2/lib/gson/FieldAttributes.java +++ b/src/com/massivecraft/mcore2/lib/gson/FieldAttributes.java @@ -17,14 +17,12 @@ package com.massivecraft.mcore2.lib.gson; import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; -import com.massivecraft.mcore2.lib.gson.internal.Pair; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; /** * A data object that stores attributes of a field. @@ -37,60 +35,30 @@ import java.util.Collections; * @since 1.4 */ public final class FieldAttributes { - private static final String MAX_CACHE_PROPERTY_NAME = - "com.google.gson.annotation_cache_size_hint"; - - private static final Cache, String>, Collection> ANNOTATION_CACHE = - new LruCache,String>, Collection>(getMaxCacheSize()); - - private final Class declaringClazz; private final Field field; - private final Class declaredType; - private final boolean isSynthetic; - private final int modifiers; - private final String name; - - // Fields used for lazy initialization - private Type genericType; - private Collection annotations; /** * Constructs a Field Attributes object from the {@code f}. * * @param f the field to pull attributes from */ - FieldAttributes(Class declaringClazz, Field f) { - this.declaringClazz = $Gson$Preconditions.checkNotNull(declaringClazz); - this.name = f.getName(); - this.declaredType = f.getType(); - this.isSynthetic = f.isSynthetic(); - this.modifiers = f.getModifiers(); + public FieldAttributes(Field f) { + $Gson$Preconditions.checkNotNull(f); this.field = f; } - private static int getMaxCacheSize() { - final int defaultMaxCacheSize = 2000; - try { - String propertyValue = System.getProperty( - MAX_CACHE_PROPERTY_NAME, String.valueOf(defaultMaxCacheSize)); - return Integer.parseInt(propertyValue); - } catch (NumberFormatException e) { - return defaultMaxCacheSize; - } - } - /** * @return the declaring class that contains this field */ public Class getDeclaringClass() { - return declaringClazz; + return field.getDeclaringClass(); } /** * @return the name of the field */ public String getName() { - return name; + return field.getName(); } /** @@ -110,10 +78,7 @@ public final class FieldAttributes { * @return the specific type declared for this field */ public Type getDeclaredType() { - if (genericType == null) { - genericType = field.getGenericType(); - } - return genericType; + return field.getGenericType(); } /** @@ -133,7 +98,7 @@ public final class FieldAttributes { * @return the specific class object that was declared for the field */ public Class getDeclaredClass() { - return declaredType; + return field.getType(); } /** @@ -144,7 +109,7 @@ public final class FieldAttributes { * @return the annotation instance if it is bound to the field; otherwise {@code null} */ public T getAnnotation(Class annotation) { - return getAnnotationFromArray(getAnnotations(), annotation); + return field.getAnnotation(annotation); } /** @@ -154,17 +119,7 @@ public final class FieldAttributes { * @since 1.4 */ public Collection getAnnotations() { - if (annotations == null) { - Pair, String> key = new Pair, String>(declaringClazz, name); - Collection cachedValue = ANNOTATION_CACHE.getElement(key); - if (cachedValue == null) { - cachedValue = Collections.unmodifiableCollection( - Arrays.asList(field.getAnnotations())); - ANNOTATION_CACHE.addElement(key, cachedValue); - } - annotations = cachedValue; - } - return annotations; + return Arrays.asList(field.getAnnotations()); } /** @@ -178,7 +133,7 @@ public final class FieldAttributes { * @see java.lang.reflect.Modifier */ public boolean hasModifier(int modifier) { - return (modifiers & modifier) != 0; + return (field.getModifiers() & modifier) != 0; } /** @@ -198,25 +153,6 @@ public final class FieldAttributes { * @return true if the field is synthetic; otherwise false */ boolean isSynthetic() { - return isSynthetic; - } - - /** - * @deprecated remove this when {@link FieldNamingStrategy} is deleted. - */ - @Deprecated - Field getFieldObject() { - return field; - } - - @SuppressWarnings("unchecked") - private static T getAnnotationFromArray( - Collection annotations, Class annotation) { - for (Annotation a : annotations) { - if (a.annotationType() == annotation) { - return (T) a; - } - } - return null; + return field.isSynthetic(); } } diff --git a/src/com/massivecraft/mcore2/lib/gson/FieldNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/FieldNamingPolicy.java index 906221a7..9df29c49 100644 --- a/src/com/massivecraft/mcore2/lib/gson/FieldNamingPolicy.java +++ b/src/com/massivecraft/mcore2/lib/gson/FieldNamingPolicy.java @@ -16,6 +16,10 @@ package com.massivecraft.mcore2.lib.gson; +import java.lang.reflect.Field; + +import com.massivecraft.mcore2.lib.gson.FieldNamingStrategy; + /** * An enumeration that defines a few standard naming conventions for JSON field names. * This enumeration should be used in conjunction with {@link com.massivecraft.mcore2.lib.gson.GsonBuilder} @@ -25,7 +29,18 @@ package com.massivecraft.mcore2.lib.gson; * @author Inderjeet Singh * @author Joel Leitch */ -public enum FieldNamingPolicy { +public enum FieldNamingPolicy implements FieldNamingStrategy { + + /** + * Using this naming policy with Gson will ensure that the field name is + * unchanged. + */ + IDENTITY() { + public String translateName(Field f) { + return f.getName(); + } + }, + /** * Using this naming policy with Gson will ensure that the first "letter" of the Java * field name is capitalized when serialized to its JSON form. @@ -36,8 +51,11 @@ public enum FieldNamingPolicy { *
  • _someFieldName ---> _SomeFieldName
  • * */ - UPPER_CAMEL_CASE(new ModifyFirstLetterNamingPolicy( - ModifyFirstLetterNamingPolicy.LetterModifier.UPPER)), + UPPER_CAMEL_CASE() { + public String translateName(Field f) { + return upperCaseFirstLetter(f.getName()); + } + }, /** * Using this naming policy with Gson will ensure that the first "letter" of the Java @@ -49,11 +67,15 @@ public enum FieldNamingPolicy { *
  • someFieldName ---> Some Field Name
  • *
  • _someFieldName ---> _Some Field Name
  • * - * + * * @since 1.4 */ - UPPER_CAMEL_CASE_WITH_SPACES(new UpperCamelCaseSeparatorNamingPolicy(" ")), - + UPPER_CAMEL_CASE_WITH_SPACES() { + public String translateName(Field f) { + return upperCaseFirstLetter(separateCamelCase(f.getName(), " ")); + } + }, + /** * Using this naming policy with Gson will modify the Java Field name from its camel cased * form to a lower case field name where each word is separated by an underscore (_). @@ -66,8 +88,12 @@ public enum FieldNamingPolicy { *
  • aURL ---> a_u_r_l
  • * */ - LOWER_CASE_WITH_UNDERSCORES(new LowerCamelCaseSeparatorNamingPolicy("_")), - + LOWER_CASE_WITH_UNDERSCORES() { + public String translateName(Field f) { + return separateCamelCase(f.getName(), "_").toLowerCase(); + } + }, + /** * Using this naming policy with Gson will modify the Java Field name from its camel cased * form to a lower case field name where each word is separated by a dash (-). @@ -85,15 +111,60 @@ public enum FieldNamingPolicy { * {@code myobject.my-field} will result in an unintended javascript expression. * @since 1.4 */ - LOWER_CASE_WITH_DASHES(new LowerCamelCaseSeparatorNamingPolicy("-")); + LOWER_CASE_WITH_DASHES() { + public String translateName(Field f) { + return separateCamelCase(f.getName(), "-").toLowerCase(); + } + }; - private final FieldNamingStrategy2 namingPolicy; - - private FieldNamingPolicy(FieldNamingStrategy2 namingPolicy) { - this.namingPolicy = namingPolicy; + /** + * Converts the field name that uses camel-case define word separation into + * separate words that are separated by the provided {@code separatorString}. + */ + private static String separateCamelCase(String name, String separator) { + StringBuilder translation = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char character = name.charAt(i); + if (Character.isUpperCase(character) && translation.length() != 0) { + translation.append(separator); + } + translation.append(character); + } + return translation.toString(); } - FieldNamingStrategy2 getFieldNamingPolicy() { - return namingPolicy; + /** + * Ensures the JSON field names begins with an upper case letter. + */ + private static String upperCaseFirstLetter(String name) { + StringBuilder fieldNameBuilder = new StringBuilder(); + int index = 0; + char firstCharacter = name.charAt(index); + + while (index < name.length() - 1) { + if (Character.isLetter(firstCharacter)) { + break; + } + + fieldNameBuilder.append(firstCharacter); + firstCharacter = name.charAt(++index); + } + + if (index == name.length()) { + return fieldNameBuilder.toString(); + } + + if (!Character.isUpperCase(firstCharacter)) { + String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), name, ++index); + return fieldNameBuilder.append(modifiedTarget).toString(); + } else { + return name; + } } -} + + private static String modifyString(char firstCharacter, String srcString, int indexOfSubstring) { + return (indexOfSubstring < srcString.length()) + ? firstCharacter + srcString.substring(indexOfSubstring) + : String.valueOf(firstCharacter); + } +} \ No newline at end of file diff --git a/src/com/massivecraft/mcore2/lib/gson/FieldNamingStrategy2.java b/src/com/massivecraft/mcore2/lib/gson/FieldNamingStrategy2.java deleted file mode 100644 index 75de778a..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/FieldNamingStrategy2.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -/** - * The new mechanism for providing custom field naming in Gson. This allows the client code - * to translate field names into a particular convention that is not supported as a normal - * Java field declaration rules. For example, Java does not support "-" characters in a - * field name. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -interface FieldNamingStrategy2 { - - /** - * Translates the field name into its JSON field name representation. - * - * @param f the field that is being translated - * @return the translated field name. - */ - public String translateName(FieldAttributes f); -} diff --git a/src/com/massivecraft/mcore2/lib/gson/FieldNamingStrategy2Adapter.java b/src/com/massivecraft/mcore2/lib/gson/FieldNamingStrategy2Adapter.java deleted file mode 100644 index 94699762..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/FieldNamingStrategy2Adapter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; - -/** - * Adapts the old FieldNamingStrategy to the new {@link FieldNamingStrategy2} - * type. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class FieldNamingStrategy2Adapter implements FieldNamingStrategy2 { - private final FieldNamingStrategy adaptee; - - FieldNamingStrategy2Adapter(FieldNamingStrategy adaptee) { - this.adaptee = $Gson$Preconditions.checkNotNull(adaptee); - } - - @SuppressWarnings("deprecation") - public String translateName(FieldAttributes f) { - return adaptee.translateName(f.getFieldObject()); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/Gson.java b/src/com/massivecraft/mcore2/lib/gson/Gson.java index 7fcc8b4e..23950bbd 100644 --- a/src/com/massivecraft/mcore2/lib/gson/Gson.java +++ b/src/com/massivecraft/mcore2/lib/gson/Gson.java @@ -16,8 +16,26 @@ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.FieldNamingPolicy; +import com.massivecraft.mcore2.lib.gson.FieldNamingStrategy; +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.GsonBuilder; +import com.massivecraft.mcore2.lib.gson.InstanceCreator; +import com.massivecraft.mcore2.lib.gson.JsonDeserializationContext; +import com.massivecraft.mcore2.lib.gson.JsonDeserializer; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonIOException; +import com.massivecraft.mcore2.lib.gson.JsonNull; +import com.massivecraft.mcore2.lib.gson.JsonParseException; +import com.massivecraft.mcore2.lib.gson.JsonSerializationContext; +import com.massivecraft.mcore2.lib.gson.JsonSerializer; +import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; +import com.massivecraft.mcore2.lib.gson.LongSerializationPolicy; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.internal.ConstructorConstructor; -import com.massivecraft.mcore2.lib.gson.internal.ParameterizedTypeHandlerMap; +import com.massivecraft.mcore2.lib.gson.internal.Excluder; +import com.massivecraft.mcore2.lib.gson.internal.GsonInternalAccess; import com.massivecraft.mcore2.lib.gson.internal.Primitives; import com.massivecraft.mcore2.lib.gson.internal.Streams; import com.massivecraft.mcore2.lib.gson.internal.bind.ArrayTypeAdapter; @@ -25,16 +43,13 @@ import com.massivecraft.mcore2.lib.gson.internal.bind.BigDecimalTypeAdapter; import com.massivecraft.mcore2.lib.gson.internal.bind.BigIntegerTypeAdapter; import com.massivecraft.mcore2.lib.gson.internal.bind.CollectionTypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.internal.bind.DateTypeAdapter; -import com.massivecraft.mcore2.lib.gson.internal.bind.ExcludedTypeAdapterFactory; -import com.massivecraft.mcore2.lib.gson.internal.bind.JsonElementReader; -import com.massivecraft.mcore2.lib.gson.internal.bind.JsonElementWriter; +import com.massivecraft.mcore2.lib.gson.internal.bind.JsonTreeReader; +import com.massivecraft.mcore2.lib.gson.internal.bind.JsonTreeWriter; import com.massivecraft.mcore2.lib.gson.internal.bind.MapTypeAdapterFactory; -import com.massivecraft.mcore2.lib.gson.internal.bind.MiniGson; import com.massivecraft.mcore2.lib.gson.internal.bind.ObjectTypeAdapter; import com.massivecraft.mcore2.lib.gson.internal.bind.ReflectiveTypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.internal.bind.SqlDateTypeAdapter; import com.massivecraft.mcore2.lib.gson.internal.bind.TimeTypeAdapter; -import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapter; import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapters; import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; @@ -48,13 +63,12 @@ import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -101,42 +115,50 @@ import java.util.Map; * @author Joel Leitch */ public final class Gson { - @SuppressWarnings("rawtypes") - static final ParameterizedTypeHandlerMap EMPTY_MAP = - new ParameterizedTypeHandlerMap().makeUnmodifiable(); - - static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; - - // Default instances of plug-ins - static final AnonymousAndLocalClassExclusionStrategy DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY = - new AnonymousAndLocalClassExclusionStrategy(); - static final SyntheticFieldExclusionStrategy DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY = - new SyntheticFieldExclusionStrategy(true); - static final ModifierBasedExclusionStrategy DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY = - new ModifierBasedExclusionStrategy(Modifier.TRANSIENT, Modifier.STATIC); - static final FieldNamingStrategy2 DEFAULT_NAMING_POLICY = - new SerializedNameAnnotationInterceptingNamingPolicy(new JavaFieldNamingPolicy()); - - private static final ExclusionStrategy DEFAULT_EXCLUSION_STRATEGY = createExclusionStrategy(); + static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; - private final ExclusionStrategy deserializationExclusionStrategy; - private final ExclusionStrategy serializationExclusionStrategy; + /** + * This thread local guards against reentrant calls to getAdapter(). In + * certain object graphs, creating an adapter for a type may recursively + * require an adapter for the same type! Without intervention, the recursive + * lookup would stack overflow. We cheat by returning a proxy type adapter. + * The proxy is wired up once the initial adapter has been created. + */ + private final ThreadLocal, FutureTypeAdapter>> calls + = new ThreadLocal, FutureTypeAdapter>>() { + @Override protected Map, FutureTypeAdapter> initialValue() { + return new HashMap, FutureTypeAdapter>(); + } + }; + + private final Map, TypeAdapter> typeTokenCache + = Collections.synchronizedMap(new HashMap, TypeAdapter>()); + + private final List factories; private final ConstructorConstructor constructorConstructor; - /** Map containing Type or Class objects as keys */ - private final ParameterizedTypeHandlerMap> serializers; - - /** Map containing Type or Class objects as keys */ - private final ParameterizedTypeHandlerMap> deserializers; - private final boolean serializeNulls; private final boolean htmlSafe; private final boolean generateNonExecutableJson; private final boolean prettyPrinting; - private final MiniGson miniGson; + final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() { + @SuppressWarnings("unchecked") + public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException { + return (T) fromJson(json, typeOfT); + } + }; + + final JsonSerializationContext serializationContext = new JsonSerializationContext() { + public JsonElement serialize(Object src) { + return toJsonTree(src); + } + public JsonElement serialize(Object src, Type typeOfSrc) { + return toJsonTree(src, typeOfSrc); + } + }; /** * Constructs a Gson object with default configuration. The default configuration has the @@ -172,110 +194,76 @@ public final class Gson { * {@link GsonBuilder#excludeFieldsWithModifiers(int...)}. * */ - @SuppressWarnings("unchecked") public Gson() { - this(DEFAULT_EXCLUSION_STRATEGY, DEFAULT_EXCLUSION_STRATEGY, DEFAULT_NAMING_POLICY, - EMPTY_MAP, false, EMPTY_MAP, EMPTY_MAP, false, DEFAULT_JSON_NON_EXECUTABLE, true, - false, false, LongSerializationPolicy.DEFAULT, - Collections.emptyList()); + this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, + Collections.>emptyMap(), false, false, DEFAULT_JSON_NON_EXECUTABLE, + true, false, false, LongSerializationPolicy.DEFAULT, + Collections.emptyList()); } - Gson(final ExclusionStrategy deserializationExclusionStrategy, - final ExclusionStrategy serializationExclusionStrategy, - final FieldNamingStrategy2 fieldNamingPolicy, - final ParameterizedTypeHandlerMap> instanceCreators, boolean serializeNulls, - final ParameterizedTypeHandlerMap> serializers, - final ParameterizedTypeHandlerMap> deserializers, + Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy, + final Map> instanceCreators, boolean serializeNulls, boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, boolean prettyPrinting, boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy, - List typeAdapterFactories) { - this.deserializationExclusionStrategy = deserializationExclusionStrategy; - this.serializationExclusionStrategy = serializationExclusionStrategy; + List typeAdapterFactories) { this.constructorConstructor = new ConstructorConstructor(instanceCreators); this.serializeNulls = serializeNulls; - this.serializers = serializers; - this.deserializers = deserializers; this.generateNonExecutableJson = generateNonExecutableGson; this.htmlSafe = htmlSafe; this.prettyPrinting = prettyPrinting; - /* - TODO: for serialization, honor: - serializationExclusionStrategy - fieldNamingPolicy - serializeNulls - serializers - */ - TypeAdapter.Factory reflectiveTypeAdapterFactory - = new ReflectiveTypeAdapterFactory(constructorConstructor) { - @Override - public String getFieldName(Class declaringClazz, Field f, Type declaredType) { - return fieldNamingPolicy.translateName(new FieldAttributes(declaringClazz, f)); - } - @Override - public boolean serializeField(Class declaringClazz, Field f, Type declaredType) { - ExclusionStrategy strategy = Gson.this.serializationExclusionStrategy; - return !strategy.shouldSkipClass(f.getType()) - && !strategy.shouldSkipField(new FieldAttributes(declaringClazz, f)); - } + TypeAdapterFactory reflectiveTypeAdapterFactory = new ReflectiveTypeAdapterFactory( + constructorConstructor, fieldNamingPolicy, excluder); - @Override - public boolean deserializeField(Class declaringClazz, Field f, Type declaredType) { - ExclusionStrategy strategy = Gson.this.deserializationExclusionStrategy; - return !strategy.shouldSkipClass(f.getType()) - && !strategy.shouldSkipField(new FieldAttributes(declaringClazz, f)); - } - }; + ConstructorConstructor constructorConstructor = new ConstructorConstructor(); + List factories = new ArrayList(); - MiniGson.Builder builder = new MiniGson.Builder() - .withoutDefaultFactories() - .factory(TypeAdapters.STRING_FACTORY) - .factory(TypeAdapters.INTEGER_FACTORY) - .factory(TypeAdapters.BOOLEAN_FACTORY) - .factory(TypeAdapters.BYTE_FACTORY) - .factory(TypeAdapters.SHORT_FACTORY) - .factory(TypeAdapters.newFactory(long.class, Long.class, - longAdapter(longSerializationPolicy))) - .factory(TypeAdapters.newFactory(double.class, Double.class, - doubleAdapter(serializeSpecialFloatingPointValues))) - .factory(TypeAdapters.newFactory(float.class, Float.class, - floatAdapter(serializeSpecialFloatingPointValues))) - .factory(new ExcludedTypeAdapterFactory( - serializationExclusionStrategy, deserializationExclusionStrategy)) - .factory(TypeAdapters.NUMBER_FACTORY) - .factory(TypeAdapters.CHARACTER_FACTORY) - .factory(TypeAdapters.STRING_BUILDER_FACTORY) - .factory(TypeAdapters.STRING_BUFFER_FACTORY) - .typeAdapter(BigDecimal.class, new BigDecimalTypeAdapter()) - .typeAdapter(BigInteger.class, new BigIntegerTypeAdapter()) - .factory(TypeAdapters.JSON_ELEMENT_FACTORY) - .factory(ObjectTypeAdapter.FACTORY); + // built-in type adapters that cannot be overridden + factories.add(TypeAdapters.STRING_FACTORY); + factories.add(TypeAdapters.INTEGER_FACTORY); + factories.add(TypeAdapters.BOOLEAN_FACTORY); + factories.add(TypeAdapters.BYTE_FACTORY); + factories.add(TypeAdapters.SHORT_FACTORY); + factories.add(TypeAdapters.newFactory(long.class, Long.class, + longAdapter(longSerializationPolicy))); + factories.add(TypeAdapters.newFactory(double.class, Double.class, + doubleAdapter(serializeSpecialFloatingPointValues))); + factories.add(TypeAdapters.newFactory(float.class, Float.class, + floatAdapter(serializeSpecialFloatingPointValues))); + factories.add(excluder); + factories.add(TypeAdapters.NUMBER_FACTORY); + factories.add(TypeAdapters.CHARACTER_FACTORY); + factories.add(TypeAdapters.STRING_BUILDER_FACTORY); + factories.add(TypeAdapters.STRING_BUFFER_FACTORY); + factories.add(TypeAdapters.newFactory(BigDecimal.class, new BigDecimalTypeAdapter())); + factories.add(TypeAdapters.newFactory(BigInteger.class, new BigIntegerTypeAdapter())); + factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); + factories.add(ObjectTypeAdapter.FACTORY); - for (TypeAdapter.Factory factory : typeAdapterFactories) { - builder.factory(factory); - } + // user's type adapters + factories.addAll(typeAdapterFactories); - builder - .factory(new GsonToMiniGsonTypeAdapterFactory(this, serializers, deserializers)) - .factory(new CollectionTypeAdapterFactory(constructorConstructor)) - .factory(TypeAdapters.URL_FACTORY) - .factory(TypeAdapters.URI_FACTORY) - .factory(TypeAdapters.UUID_FACTORY) - .factory(TypeAdapters.LOCALE_FACTORY) - .factory(TypeAdapters.INET_ADDRESS_FACTORY) - .factory(TypeAdapters.BIT_SET_FACTORY) - .factory(DateTypeAdapter.FACTORY) - .factory(TypeAdapters.CALENDAR_FACTORY) - .factory(TimeTypeAdapter.FACTORY) - .factory(SqlDateTypeAdapter.FACTORY) - .factory(TypeAdapters.TIMESTAMP_FACTORY) - .factory(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)) - .factory(ArrayTypeAdapter.FACTORY) - .factory(TypeAdapters.ENUM_FACTORY) - .factory(reflectiveTypeAdapterFactory); + // built-in type adapters that can be overridden + factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); + factories.add(TypeAdapters.URL_FACTORY); + factories.add(TypeAdapters.URI_FACTORY); + factories.add(TypeAdapters.UUID_FACTORY); + factories.add(TypeAdapters.LOCALE_FACTORY); + factories.add(TypeAdapters.INET_ADDRESS_FACTORY); + factories.add(TypeAdapters.BIT_SET_FACTORY); + factories.add(DateTypeAdapter.FACTORY); + factories.add(TypeAdapters.CALENDAR_FACTORY); + factories.add(TimeTypeAdapter.FACTORY); + factories.add(SqlDateTypeAdapter.FACTORY); + factories.add(TypeAdapters.TIMESTAMP_FACTORY); + factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)); + factories.add(ArrayTypeAdapter.FACTORY); + factories.add(TypeAdapters.ENUM_FACTORY); + factories.add(TypeAdapters.CLASS_FACTORY); + factories.add(reflectiveTypeAdapterFactory); - this.miniGson = builder.build(); + this.factories = Collections.unmodifiableList(factories); } private TypeAdapter doubleAdapter(boolean serializeSpecialFloatingPointValues) { @@ -283,21 +271,21 @@ public final class Gson { return TypeAdapters.DOUBLE; } return new TypeAdapter() { - @Override public Double read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + @Override public Double read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } - return reader.nextDouble(); + return in.nextDouble(); } - @Override public void write(JsonWriter writer, Number value) throws IOException { + @Override public void write(JsonWriter out, Number value) throws IOException { if (value == null) { - writer.nullValue(); // TODO: better policy here? + out.nullValue(); return; } double doubleValue = value.doubleValue(); checkValidFloatingPoint(doubleValue); - writer.value(value); + out.value(value); } }; } @@ -307,21 +295,21 @@ public final class Gson { return TypeAdapters.FLOAT; } return new TypeAdapter() { - @Override public Float read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + @Override public Float read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } - return (float) reader.nextDouble(); + return (float) in.nextDouble(); } - @Override public void write(JsonWriter writer, Number value) throws IOException { + @Override public void write(JsonWriter out, Number value) throws IOException { if (value == null) { - writer.nullValue(); // TODO: better policy here? + out.nullValue(); return; } float floatValue = value.floatValue(); checkValidFloatingPoint(floatValue); - writer.value(value); + out.value(value); } }; } @@ -339,29 +327,93 @@ public final class Gson { return TypeAdapters.LONG; } return new TypeAdapter() { - @Override public Number read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + @Override public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } - return reader.nextLong(); + return in.nextLong(); } - @Override public void write(JsonWriter writer, Number value) throws IOException { + @Override public void write(JsonWriter out, Number value) throws IOException { if (value == null) { - writer.nullValue(); // TODO: better policy here? + out.nullValue(); return; } - writer.value(value.toString()); + out.value(value.toString()); } }; } - private static ExclusionStrategy createExclusionStrategy() { - List strategies = new LinkedList(); - strategies.add(DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY); - strategies.add(DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY); - strategies.add(DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY); - return new DisjunctionExclusionStrategy(strategies); + /** + * Returns the type adapter for {@code} type. + * + * @throws IllegalArgumentException if this GSON cannot serialize and + * deserialize {@code type}. + */ + @SuppressWarnings("unchecked") + public TypeAdapter getAdapter(TypeToken type) { + TypeAdapter cached = typeTokenCache.get(type); + if (cached != null) { + return (TypeAdapter) cached; + } + + Map, FutureTypeAdapter> threadCalls = calls.get(); + // the key and value type parameters always agree + FutureTypeAdapter ongoingCall = (FutureTypeAdapter) threadCalls.get(type); + if (ongoingCall != null) { + return ongoingCall; + } + + FutureTypeAdapter call = new FutureTypeAdapter(); + threadCalls.put(type, call); + try { + for (TypeAdapterFactory factory : factories) { + TypeAdapter candidate = factory.create(this, type); + if (candidate != null) { + call.setDelegate(candidate); + typeTokenCache.put(type, candidate); + return candidate; + } + } + throw new IllegalArgumentException("GSON cannot handle " + type); + } finally { + threadCalls.remove(type); + } + } + + static { + GsonInternalAccess.INSTANCE = new GsonInternalAccess() { + @Override public TypeAdapter getNextAdapter( + Gson gson, TypeAdapterFactory skipPast, TypeToken type) { + boolean skipPastFound = false; + + for (TypeAdapterFactory factory : gson.factories) { + if (!skipPastFound) { + if (factory == skipPast) { + skipPastFound = true; + } + continue; + } + + TypeAdapter candidate = factory.create(gson, type); + if (candidate != null) { + return candidate; + } + } + + throw new IllegalArgumentException("GSON cannot serialize " + type); + } + }; + } + + /** + * Returns the type adapter for {@code} type. + * + * @throws IllegalArgumentException if this GSON cannot serialize and + * deserialize {@code type}. + */ + public TypeAdapter getAdapter(Class type) { + return getAdapter(TypeToken.get(type)); } /** @@ -400,9 +452,8 @@ public final class Gson { * @return Json representation of {@code src} * @since 1.4 */ - // the caller is required to make src and typeOfSrc consistent public JsonElement toJsonTree(Object src, Type typeOfSrc) { - JsonElementWriter writer = new JsonElementWriter(); + JsonTreeWriter writer = new JsonTreeWriter(); toJson(src, typeOfSrc, writer); return writer.get(); } @@ -502,7 +553,7 @@ public final class Gson { */ @SuppressWarnings("unchecked") public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException { - TypeAdapter adapter = miniGson.getAdapter(TypeToken.get(typeOfSrc)); + TypeAdapter adapter = getAdapter(TypeToken.get(typeOfSrc)); boolean oldLenient = writer.isLenient(); writer.setLenient(true); boolean oldHtmlSafe = writer.isHtmlSafe(); @@ -683,7 +734,7 @@ public final class Gson { * @since 1.2 */ @SuppressWarnings("unchecked") -public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { + public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { JsonReader jsonReader = new JsonReader(json); T object = (T) fromJson(jsonReader, typeOfT); assertFullConsumption(object, jsonReader); @@ -718,7 +769,7 @@ public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyn try { reader.peek(); isEmpty = false; - TypeAdapter typeAdapter = (TypeAdapter) miniGson.getAdapter(TypeToken.get(typeOfT)); + TypeAdapter typeAdapter = (TypeAdapter) getAdapter(TypeToken.get(typeOfT)); return typeAdapter.read(reader); } catch (EOFException e) { /* @@ -779,26 +830,45 @@ public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyn * @since 1.3 */ @SuppressWarnings("unchecked") -public T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException { + public T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException { if (json == null) { return null; } - return (T) fromJson(new JsonElementReader(json), typeOfT); + return (T) fromJson(new JsonTreeReader(json), typeOfT); + } + + static class FutureTypeAdapter extends TypeAdapter { + private TypeAdapter delegate; + + public void setDelegate(TypeAdapter typeAdapter) { + if (delegate != null) { + throw new AssertionError(); + } + delegate = typeAdapter; + } + + @Override public T read(JsonReader in) throws IOException { + if (delegate == null) { + throw new IllegalStateException(); + } + return delegate.read(in); + } + + @Override public void write(JsonWriter out, T value) throws IOException { + if (delegate == null) { + throw new IllegalStateException(); + } + delegate.write(out, value); + } } @Override public String toString() { StringBuilder sb = new StringBuilder("{") .append("serializeNulls:").append(serializeNulls) - .append(",serializers:").append(serializers) - .append(",deserializers:").append(deserializers) - - // using the name instanceCreator instead of ObjectConstructor since the users of Gson are - // more familiar with the concept of Instance Creators. Moreover, the objectConstructor is - // just a utility class around instance creators, and its toString() only displays them. + .append("factories:").append(factories) .append(",instanceCreators:").append(constructorConstructor) .append("}"); return sb.toString(); } - } diff --git a/src/com/massivecraft/mcore2/lib/gson/GsonBuilder.java b/src/com/massivecraft/mcore2/lib/gson/GsonBuilder.java index faf19ba8..987e0cf2 100644 --- a/src/com/massivecraft/mcore2/lib/gson/GsonBuilder.java +++ b/src/com/massivecraft/mcore2/lib/gson/GsonBuilder.java @@ -16,22 +16,35 @@ package com.massivecraft.mcore2.lib.gson; -import com.massivecraft.mcore2.lib.gson.DefaultTypeAdapters.DefaultDateTypeAdapter; -import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; -import com.massivecraft.mcore2.lib.gson.internal.ParameterizedTypeHandlerMap; -import com.massivecraft.mcore2.lib.gson.internal.Primitives; -import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapter; - import java.lang.reflect.Type; import java.sql.Timestamp; import java.text.DateFormat; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.Date; -import java.util.HashSet; -import java.util.LinkedList; +import java.util.HashMap; import java.util.List; -import java.util.Set; +import java.util.Map; + +import com.massivecraft.mcore2.lib.gson.DefaultDateTypeAdapter; +import com.massivecraft.mcore2.lib.gson.ExclusionStrategy; +import com.massivecraft.mcore2.lib.gson.FieldNamingPolicy; +import com.massivecraft.mcore2.lib.gson.FieldNamingStrategy; +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.GsonBuilder; +import com.massivecraft.mcore2.lib.gson.InstanceCreator; +import com.massivecraft.mcore2.lib.gson.JsonDeserializer; +import com.massivecraft.mcore2.lib.gson.JsonObject; +import com.massivecraft.mcore2.lib.gson.JsonSerializer; +import com.massivecraft.mcore2.lib.gson.LongSerializationPolicy; +import com.massivecraft.mcore2.lib.gson.TreeTypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; +import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; +import com.massivecraft.mcore2.lib.gson.internal.Excluder; +import com.massivecraft.mcore2.lib.gson.internal.Primitives; +import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapters; +import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; /** *

    Use this builder to construct a {@link Gson} instance when you need to set configuration @@ -65,40 +78,24 @@ import java.util.Set; * * @author Inderjeet Singh * @author Joel Leitch + * @author Jesse Wilson */ public final class GsonBuilder { - private static final InnerClassExclusionStrategy innerClassExclusionStrategy = - new InnerClassExclusionStrategy(); - private static final ExposeAnnotationDeserializationExclusionStrategy - exposeAnnotationDeserializationExclusionStrategy = - new ExposeAnnotationDeserializationExclusionStrategy(); - private static final ExposeAnnotationSerializationExclusionStrategy - exposeAnnotationSerializationExclusionStrategy = - new ExposeAnnotationSerializationExclusionStrategy(); - - private final Set serializeExclusionStrategies = - new HashSet(); - private final Set deserializeExclusionStrategies = - new HashSet(); - - private double ignoreVersionsAfter; - private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy; - private boolean serializeInnerClasses; - private boolean excludeFieldsWithoutExposeAnnotation; - private LongSerializationPolicy longSerializationPolicy; - private FieldNamingStrategy2 fieldNamingPolicy; - private final ParameterizedTypeHandlerMap> instanceCreators; - private final ParameterizedTypeHandlerMap> serializers; - private final ParameterizedTypeHandlerMap> deserializers; - private final List typeAdapterFactories - = new ArrayList(); + private Excluder excluder = Excluder.DEFAULT; + private LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT; + private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY; + private final Map> instanceCreators + = new HashMap>(); + private final List factories = new ArrayList(); + /** tree-style hierarchy factories. These come after factories for backwards compatibility. */ + private final List hierarchyFactories = new ArrayList(); private boolean serializeNulls; private String datePattern; - private int dateStyle; - private int timeStyle; - private boolean complexMapKeySerialization = false; + private int dateStyle = DateFormat.DEFAULT; + private int timeStyle = DateFormat.DEFAULT; + private boolean complexMapKeySerialization; private boolean serializeSpecialFloatingPointValues; - private boolean escapeHtmlChars; + private boolean escapeHtmlChars = true; private boolean prettyPrinting; private boolean generateNonExecutableJson; @@ -109,29 +106,6 @@ public final class GsonBuilder { * {@link #create()}. */ public GsonBuilder() { - // add default exclusion strategies - deserializeExclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY); - deserializeExclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY); - serializeExclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY); - serializeExclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY); - - // setup default values - ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS; - serializeInnerClasses = true; - prettyPrinting = false; - escapeHtmlChars = true; - modifierBasedExclusionStrategy = Gson.DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY; - excludeFieldsWithoutExposeAnnotation = false; - longSerializationPolicy = LongSerializationPolicy.DEFAULT; - fieldNamingPolicy = Gson.DEFAULT_NAMING_POLICY; - instanceCreators = new ParameterizedTypeHandlerMap>(); - serializers = new ParameterizedTypeHandlerMap>(); - deserializers = new ParameterizedTypeHandlerMap>(); - serializeNulls = false; - dateStyle = DateFormat.DEFAULT; - timeStyle = DateFormat.DEFAULT; - serializeSpecialFloatingPointValues = false; - generateNonExecutableJson = false; } /** @@ -142,7 +116,7 @@ public final class GsonBuilder { * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ public GsonBuilder setVersion(double ignoreVersionsAfter) { - this.ignoreVersionsAfter = ignoreVersionsAfter; + excluder = excluder.withVersion(ignoreVersionsAfter); return this; } @@ -158,7 +132,7 @@ public final class GsonBuilder { * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ public GsonBuilder excludeFieldsWithModifiers(int... modifiers) { - modifierBasedExclusionStrategy = new ModifierBasedExclusionStrategy(modifiers); + excluder = excluder.withModifiers(modifiers); return this; } @@ -183,7 +157,7 @@ public final class GsonBuilder { * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ public GsonBuilder excludeFieldsWithoutExposeAnnotation() { - excludeFieldsWithoutExposeAnnotation = true; + excluder = excluder.excludeFieldsWithoutExposeAnnotation(); return this; } @@ -287,7 +261,7 @@ public final class GsonBuilder { * @since 1.3 */ public GsonBuilder disableInnerClassSerialization() { - serializeInnerClasses = false; + excluder = excluder.disableInnerClassSerialization(); return this; } @@ -313,7 +287,8 @@ public final class GsonBuilder { * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) { - return setFieldNamingStrategy(namingConvention.getFieldNamingPolicy()); + this.fieldNamingPolicy = namingConvention; + return this; } /** @@ -325,19 +300,7 @@ public final class GsonBuilder { * @since 1.3 */ public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) { - return setFieldNamingStrategy(new FieldNamingStrategy2Adapter(fieldNamingStrategy)); - } - - /** - * Configures Gson to apply a specific naming policy strategy to an object's field during - * serialization and deserialization. - * - * @param fieldNamingStrategy the actual naming strategy to apply to the fields - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - */ - GsonBuilder setFieldNamingStrategy(FieldNamingStrategy2 fieldNamingStrategy) { - this.fieldNamingPolicy = - new SerializedNameAnnotationInterceptingNamingPolicy(fieldNamingStrategy); + this.fieldNamingPolicy = fieldNamingStrategy; return this; } @@ -352,9 +315,9 @@ public final class GsonBuilder { * @since 1.4 */ public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) { - List strategyList = Arrays.asList(strategies); - serializeExclusionStrategies.addAll(strategyList); - deserializeExclusionStrategies.addAll(strategyList); + for (ExclusionStrategy strategy : strategies) { + excluder = excluder.withExclusionStrategy(strategy, true, true); + } return this; } @@ -371,7 +334,7 @@ public final class GsonBuilder { * @since 1.7 */ public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy) { - serializeExclusionStrategies.add(strategy); + excluder = excluder.withExclusionStrategy(strategy, true, false); return this; } @@ -388,7 +351,7 @@ public final class GsonBuilder { * @since 1.7 */ public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strategy) { - deserializeExclusionStrategies.add(strategy); + excluder = excluder.withExclusionStrategy(strategy, false, true); return this; } @@ -481,147 +444,78 @@ public final class GsonBuilder { /** * Configures Gson for custom serialization or deserialization. This method combines the - * registration of an {@link InstanceCreator}, {@link JsonSerializer}, and a + * registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a * {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements - * all the required interfaces for custom serialization with Gson. If an instance creator, - * serializer or deserializer was previously registered for the specified {@code type}, it is - * overwritten. + * all the required interfaces for custom serialization with Gson. If a type adapter was + * previously registered for the specified {@code type}, it is overwritten. * * @param type the type definition for the type adapter being registered - * @param typeAdapter This object must implement at least one of the {@link InstanceCreator}, - * {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces. + * @param typeAdapter This object must implement at least one of the {@link TypeAdapter}, + * {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ + @SuppressWarnings({"unchecked", "rawtypes"}) public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { - return registerTypeAdapter(type, typeAdapter, false); - } - - private GsonBuilder registerTypeAdapter(Type type, Object typeAdapter, boolean isSystem) { $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof InstanceCreator - || typeAdapter instanceof TypeAdapter.Factory); + || typeAdapter instanceof TypeAdapter); if (Primitives.isPrimitive(type) || Primitives.isWrapperType(type)) { throw new IllegalArgumentException( "Cannot register type adapters for " + type); } if (typeAdapter instanceof InstanceCreator) { - registerInstanceCreator(type, (InstanceCreator) typeAdapter, isSystem); + instanceCreators.put(type, (InstanceCreator) typeAdapter); } - if (typeAdapter instanceof JsonSerializer) { - registerSerializer(type, (JsonSerializer) typeAdapter, isSystem); + if (typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer) { + TypeToken typeToken = TypeToken.get(type); + factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter)); } - if (typeAdapter instanceof JsonDeserializer) { - registerDeserializer(type, (JsonDeserializer) typeAdapter, isSystem); - } - if (typeAdapter instanceof TypeAdapter.Factory) { - typeAdapterFactories.add((TypeAdapter.Factory) typeAdapter); + if (typeAdapter instanceof TypeAdapter) { + factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter)); } return this; } /** - * Configures Gson to use a custom {@link InstanceCreator} for the specified type. If an instance - * creator was previously registered for the specified class, it is overwritten. Since this method - * takes a type instead of a Class object, it can be used to register a specific handler for a - * generic type corresponding to a raw type. + * Register a factory for type adapters. Registering a factory is useful when the type + * adapter needs to be configured based on the type of the field being processed. Gson + * is designed to handle a large number of factories, so you should consider registering + * them to be at par with registering an individual type adapter. * - * @param the type for which instance creator is being registered - * @param typeOfT The Type definition for T - * @param instanceCreator the instance creator for T - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + * @since 2.1 */ - private GsonBuilder registerInstanceCreator(Type typeOfT, - InstanceCreator instanceCreator, boolean isSystem) { - instanceCreators.register(typeOfT, instanceCreator, isSystem); - return this; - } - - /** - * Configures Gson to use a custom JSON serializer for the specified type. You should use this - * method if you want to register different serializers for different generic types corresponding - * to a raw type. - * - * @param the type for which the serializer is being registered - * @param typeOfT The type definition for T - * @param serializer the custom serializer - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - */ - private GsonBuilder registerSerializer(Type typeOfT, JsonSerializer serializer, - boolean isSystem) { - serializers.register(typeOfT, serializer, isSystem); - return this; - } - - /** - * Configures Gson to use a custom JSON deserializer for the specified type. You should use this - * method if you want to register different deserializers for different generic types - * corresponding to a raw type. - * - * @param the type for which the deserializer is being registered - * @param typeOfT The type definition for T - * @param deserializer the custom deserializer - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - */ - private GsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer deserializer, - boolean isSystem) { - deserializers.register(typeOfT, new JsonDeserializerExceptionWrapper(deserializer), isSystem); + public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) { + factories.add(factory); return this; } /** * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy. - * This method combines the registration of an {@link InstanceCreator}, {@link JsonSerializer}, - * and a {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} - * implements all the required interfaces for custom serialization with Gson. - * If an instance creator, serializer or deserializer was previously registered for the specified - * type hierarchy, it is overwritten. If an instance creator, serializer or deserializer is - * registered for a specific type in the type hierarchy, it will be invoked instead of the one - * registered for the type hierarchy. + * This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and + * a {@link JsonDeserializer}. If a type adapter was previously registered for the specified + * type hierarchy, it is overridden. If a type adapter is registered for a specific type in + * the type hierarchy, it will be invoked instead of the one registered for the type hierarchy. * * @param baseType the class definition for the type adapter being registered for the base class * or interface - * @param typeAdapter This object must implement at least one of the {@link InstanceCreator}, - * {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces. + * @param typeAdapter This object must implement at least one of {@link TypeAdapter}, + * {@link JsonSerializer} or {@link JsonDeserializer} interfaces. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.7 */ + @SuppressWarnings({"unchecked", "rawtypes"}) public GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAdapter) { - return registerTypeHierarchyAdapter(baseType, typeAdapter, false); - } - - private GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAdapter, - boolean isSystem) { $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer - || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof InstanceCreator); - if (typeAdapter instanceof InstanceCreator) { - registerInstanceCreatorForTypeHierarchy(baseType, (InstanceCreator) typeAdapter, isSystem); + || typeAdapter instanceof JsonDeserializer + || typeAdapter instanceof TypeAdapter); + if (typeAdapter instanceof JsonDeserializer || typeAdapter instanceof JsonSerializer) { + hierarchyFactories.add(0, + TreeTypeAdapter.newTypeHierarchyFactory(baseType, typeAdapter)); } - if (typeAdapter instanceof JsonSerializer) { - registerSerializerForTypeHierarchy(baseType, (JsonSerializer) typeAdapter, isSystem); + if (typeAdapter instanceof TypeAdapter) { + factories.add(TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter)typeAdapter)); } - if (typeAdapter instanceof JsonDeserializer) { - registerDeserializerForTypeHierarchy(baseType, (JsonDeserializer) typeAdapter, isSystem); - } - return this; - } - - private GsonBuilder registerInstanceCreatorForTypeHierarchy(Class classOfT, - InstanceCreator instanceCreator, boolean isSystem) { - instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator, isSystem); - return this; - } - - private GsonBuilder registerSerializerForTypeHierarchy(Class classOfT, - JsonSerializer serializer, boolean isSystem) { - serializers.registerForTypeHierarchy(classOfT, serializer, isSystem); - return this; - } - - private GsonBuilder registerDeserializerForTypeHierarchy(Class classOfT, - JsonDeserializer deserializer, boolean isSystem) { - deserializers.registerForTypeHierarchy(classOfT, - new JsonDeserializerExceptionWrapper(deserializer), isSystem); return this; } @@ -657,61 +551,31 @@ public final class GsonBuilder { * @return an instance of Gson configured with the options currently set in this builder */ public Gson create() { - List deserializationStrategies = - new LinkedList(deserializeExclusionStrategies); - List serializationStrategies = - new LinkedList(serializeExclusionStrategies); - deserializationStrategies.add(modifierBasedExclusionStrategy); - serializationStrategies.add(modifierBasedExclusionStrategy); + List factories = new ArrayList(); + factories.addAll(this.factories); + Collections.reverse(factories); + factories.addAll(this.hierarchyFactories); + addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories); - if (!serializeInnerClasses) { - deserializationStrategies.add(innerClassExclusionStrategy); - serializationStrategies.add(innerClassExclusionStrategy); - } - if (ignoreVersionsAfter != VersionConstants.IGNORE_VERSIONS) { - VersionExclusionStrategy versionExclusionStrategy = - new VersionExclusionStrategy(ignoreVersionsAfter); - deserializationStrategies.add(versionExclusionStrategy); - serializationStrategies.add(versionExclusionStrategy); - } - if (excludeFieldsWithoutExposeAnnotation) { - deserializationStrategies.add(exposeAnnotationDeserializationExclusionStrategy); - serializationStrategies.add(exposeAnnotationSerializationExclusionStrategy); - } - addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, serializers, deserializers); - - return new Gson(new DisjunctionExclusionStrategy(deserializationStrategies), - new DisjunctionExclusionStrategy(serializationStrategies), - fieldNamingPolicy, instanceCreators.copyOf().makeUnmodifiable(), serializeNulls, - serializers.copyOf().makeUnmodifiable(), deserializers.copyOf().makeUnmodifiable(), - complexMapKeySerialization, generateNonExecutableJson, escapeHtmlChars, prettyPrinting, - serializeSpecialFloatingPointValues, longSerializationPolicy, typeAdapterFactories); + return new Gson(excluder, fieldNamingPolicy, instanceCreators, + serializeNulls, complexMapKeySerialization, + generateNonExecutableJson, escapeHtmlChars, prettyPrinting, + serializeSpecialFloatingPointValues, longSerializationPolicy, factories); } - private static void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, - ParameterizedTypeHandlerMap> serializers, - ParameterizedTypeHandlerMap> deserializers) { - DefaultDateTypeAdapter dateTypeAdapter = null; + private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, + List factories) { + DefaultDateTypeAdapter dateTypeAdapter; if (datePattern != null && !"".equals(datePattern.trim())) { dateTypeAdapter = new DefaultDateTypeAdapter(datePattern); } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) { dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle); + } else { + return; } - if (dateTypeAdapter != null) { - registerIfAbsent(Date.class, serializers, dateTypeAdapter); - registerIfAbsent(Date.class, deserializers, dateTypeAdapter); - registerIfAbsent(Timestamp.class, serializers, dateTypeAdapter); - registerIfAbsent(Timestamp.class, deserializers, dateTypeAdapter); - registerIfAbsent(java.sql.Date.class, serializers, dateTypeAdapter); - registerIfAbsent(java.sql.Date.class, deserializers, dateTypeAdapter); - } - } - - private static void registerIfAbsent(Class type, - ParameterizedTypeHandlerMap adapters, T adapter) { - if (!adapters.hasSpecificHandlerFor(type)) { - adapters.register(type, adapter, false); - } + factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Date.class), dateTypeAdapter)); + factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Timestamp.class), dateTypeAdapter)); + factories.add(TreeTypeAdapter.newFactory(TypeToken.get(java.sql.Date.class), dateTypeAdapter)); } } diff --git a/src/com/massivecraft/mcore2/lib/gson/GsonToMiniGsonTypeAdapterFactory.java b/src/com/massivecraft/mcore2/lib/gson/GsonToMiniGsonTypeAdapterFactory.java deleted file mode 100644 index 0f7bcd47..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/GsonToMiniGsonTypeAdapterFactory.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.internal.ParameterizedTypeHandlerMap; -import com.massivecraft.mcore2.lib.gson.internal.Streams; -import com.massivecraft.mcore2.lib.gson.internal.bind.MiniGson; -import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapter; -import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; -import com.massivecraft.mcore2.lib.gson.stream.JsonReader; -import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; - -import java.io.IOException; -import java.lang.reflect.Type; - -final class GsonToMiniGsonTypeAdapterFactory implements TypeAdapter.Factory { - private final ParameterizedTypeHandlerMap> serializers; - private final ParameterizedTypeHandlerMap> deserializers; - private final JsonDeserializationContext deserializationContext; - private final JsonSerializationContext serializationContext; - - public GsonToMiniGsonTypeAdapterFactory(final Gson gson, - ParameterizedTypeHandlerMap> serializers, - ParameterizedTypeHandlerMap> deserializers) { - this.serializers = serializers; - this.deserializers = deserializers; - - this.deserializationContext = new JsonDeserializationContext() { - @SuppressWarnings("unchecked") - public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException { - return (T) gson.fromJson(json, typeOfT); - } - }; - - this.serializationContext = new JsonSerializationContext() { - public JsonElement serialize(Object src) { - return gson.toJsonTree(src); - } - public JsonElement serialize(Object src, Type typeOfSrc) { - return gson.toJsonTree(src, typeOfSrc); - } - }; - } - - public TypeAdapter create(final MiniGson context, final TypeToken typeToken) { - final Type type = typeToken.getType(); - - @SuppressWarnings("unchecked") // guaranteed to match typeOfT - final JsonSerializer serializer - = (JsonSerializer) serializers.getHandlerFor(type, false); - @SuppressWarnings("unchecked") // guaranteed to match typeOfT - final JsonDeserializer deserializer - = (JsonDeserializer) deserializers.getHandlerFor(type, false); - - if (serializer == null && deserializer == null) { - return null; - } - - return new TypeAdapter() { - /** - * The delegate is lazily created because it may not be needed, and - * creating it may fail. - */ - private TypeAdapter delegate; - - @Override public T read(JsonReader reader) throws IOException { - if (deserializer == null) { - return delegate().read(reader); - } - JsonElement value = Streams.parse(reader); - if (value.isJsonNull()) { - return null; - } - return deserializer.deserialize(value, type, deserializationContext); - } - - @Override public void write(JsonWriter writer, T value) throws IOException { - if (serializer == null) { - delegate().write(writer, value); - return; - } - if (value == null) { - writer.nullValue(); - return; - } - JsonElement element = serializer.serialize(value, type, serializationContext); - Streams.write(element, writer); - } - - private TypeAdapter delegate() { - TypeAdapter d = delegate; - return d != null - ? d - : (delegate = context.getNextAdapter(GsonToMiniGsonTypeAdapterFactory.this, typeToken)); - } - }; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/InnerClassExclusionStrategy.java b/src/com/massivecraft/mcore2/lib/gson/InnerClassExclusionStrategy.java deleted file mode 100644 index 5cafee49..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/InnerClassExclusionStrategy.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import java.lang.reflect.Modifier; - -/** - * Strategy for excluding inner classes. - * - * @author Joel Leitch - */ -final class InnerClassExclusionStrategy implements ExclusionStrategy { - - public boolean shouldSkipField(FieldAttributes f) { - return isInnerClass(f.getDeclaredClass()); - } - - public boolean shouldSkipClass(Class clazz) { - return isInnerClass(clazz); - } - - private boolean isInnerClass(Class clazz) { - return clazz.isMemberClass() && !isStatic(clazz); - } - - private boolean isStatic(Class clazz) { - return (clazz.getModifiers() & Modifier.STATIC) != 0; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/InstanceCreator.java b/src/com/massivecraft/mcore2/lib/gson/InstanceCreator.java index 1cdca5ed..a03ec1ee 100644 --- a/src/com/massivecraft/mcore2/lib/gson/InstanceCreator.java +++ b/src/com/massivecraft/mcore2/lib/gson/InstanceCreator.java @@ -18,6 +18,8 @@ package com.massivecraft.mcore2.lib.gson; import java.lang.reflect.Type; +import com.massivecraft.mcore2.lib.gson.GsonBuilder; + /** * This interface is implemented to create instances of a class that does not define a no-args * constructor. If you can modify the class, you should instead add a private, or public diff --git a/src/com/massivecraft/mcore2/lib/gson/JavaFieldNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/JavaFieldNamingPolicy.java deleted file mode 100644 index afb18bd7..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/JavaFieldNamingPolicy.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collection; - -/** - * A simple implementation of the {@link FieldNamingStrategy2} interface such that it does not - * perform any string translation of the incoming field name. - * - *

    The following is an example:

    - * - *
    - * class IntWrapper {
    - *   public int integerField = 0;
    - * }
    - *
    - * JavaFieldNamingPolicy policy = new JavaFieldNamingPolicy();
    - * String translatedFieldName =
    - *     policy.translateName(IntWrapper.class.getField("integerField"));
    - *
    - * assert("integerField".equals(translatedFieldName));
    - * 
    - * - *

    This is the default {@link FieldNamingStrategy2} used by Gson.

    - * - * @author Joel Leitch - */ -final class JavaFieldNamingPolicy extends RecursiveFieldNamingPolicy { - - @Override - protected String translateName(String target, Type fieldType, Collection annotations) { - return target; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonArray.java b/src/com/massivecraft/mcore2/lib/gson/JsonArray.java index 5a04d8a4..b2d6ecd9 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonArray.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonArray.java @@ -22,6 +22,11 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import com.massivecraft.mcore2.lib.gson.JsonArray; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonNull; +import com.massivecraft.mcore2.lib.gson.JsonPrimitive; + /** * A class representing an array type in Json. An array is a list of {@link JsonElement}s each of * which can be of a different type. This is an ordered list, meaning that the order in which diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonDeserializationContext.java b/src/com/massivecraft/mcore2/lib/gson/JsonDeserializationContext.java index 39a13292..5f07068a 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonDeserializationContext.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonDeserializationContext.java @@ -18,6 +18,11 @@ package com.massivecraft.mcore2.lib.gson; import java.lang.reflect.Type; +import com.massivecraft.mcore2.lib.gson.JsonDeserializationContext; +import com.massivecraft.mcore2.lib.gson.JsonDeserializer; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonParseException; + /** * Context for deserialization that is passed to a custom deserializer during invocation of its * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonDeserializer.java b/src/com/massivecraft/mcore2/lib/gson/JsonDeserializer.java index 31a9e462..fafe169c 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonDeserializer.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonDeserializer.java @@ -18,6 +18,12 @@ package com.massivecraft.mcore2.lib.gson; import java.lang.reflect.Type; +import com.massivecraft.mcore2.lib.gson.GsonBuilder; +import com.massivecraft.mcore2.lib.gson.JsonDeserializationContext; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonParseException; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; + /** *

    Interface representing a custom deserializer for Json. You should write a custom * deserializer, if you are not happy with the default deserialization done by Gson. You will @@ -61,6 +67,9 @@ import java.lang.reflect.Type; * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdDeserializer()).create(); * * + *

    New applications should prefer {@link TypeAdapter}, whose streaming API + * is more efficient than this interface's tree API. + * * @author Inderjeet Singh * @author Joel Leitch * diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonDeserializerExceptionWrapper.java b/src/com/massivecraft/mcore2/lib/gson/JsonDeserializerExceptionWrapper.java deleted file mode 100644 index 7e57eebf..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/JsonDeserializerExceptionWrapper.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; - -import java.lang.reflect.Type; - -/** - * Decorators a {@code JsonDeserializer} instance with exception handling. This wrapper class - * ensures that a {@code JsonDeserializer} will not propagate any exception other than a - * {@link JsonParseException}. - * - * @param type of the deserializer being wrapped. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class JsonDeserializerExceptionWrapper implements JsonDeserializer { - - private final JsonDeserializer delegate; - - /** - * Returns a wrapped {@link JsonDeserializer} object that has been decorated with - * {@link JsonParseException} handling. - * - * @param delegate the {@code JsonDeserializer} instance to be wrapped. - * @throws IllegalArgumentException if {@code delegate} is {@code null}. - */ - JsonDeserializerExceptionWrapper(JsonDeserializer delegate) { - this.delegate = $Gson$Preconditions.checkNotNull(delegate); - } - - public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - try { - return delegate.deserialize(json, typeOfT, context); - } catch (JsonParseException e) { - // just rethrow the exception - throw e; - } catch (Exception e) { - // rethrow as a JsonParseException - StringBuilder errorMsg = new StringBuilder() - .append("The JsonDeserializer ") - .append(delegate) - .append(" failed to deserialize json object ") - .append(json) - .append(" given the type ") - .append(typeOfT); - throw new JsonParseException(errorMsg.toString(), e); - } - } - - @Override - public String toString() { - return delegate.toString(); - } -} \ No newline at end of file diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonElement.java b/src/com/massivecraft/mcore2/lib/gson/JsonElement.java index a748b240..c001b545 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonElement.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonElement.java @@ -16,6 +16,10 @@ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.JsonArray; +import com.massivecraft.mcore2.lib.gson.JsonNull; +import com.massivecraft.mcore2.lib.gson.JsonObject; +import com.massivecraft.mcore2.lib.gson.JsonPrimitive; import com.massivecraft.mcore2.lib.gson.internal.Streams; import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonElementVisitor.java b/src/com/massivecraft/mcore2/lib/gson/JsonElementVisitor.java deleted file mode 100644 index 37fb66c2..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/JsonElementVisitor.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import java.io.IOException; - -/** - * Definition of a visitor for a JsonElement tree. - * - * @author Inderjeet Singh - */ -interface JsonElementVisitor { - void visitPrimitive(JsonPrimitive primitive) throws IOException; - void visitNull() throws IOException; - - void startArray(JsonArray array) throws IOException; - void visitArrayMember(JsonArray parent, JsonPrimitive member, boolean isFirst) throws IOException; - void visitArrayMember(JsonArray parent, JsonArray member, boolean isFirst) throws IOException; - void visitArrayMember(JsonArray parent, JsonObject member, boolean isFirst) throws IOException; - void visitNullArrayMember(JsonArray parent, boolean isFirst) throws IOException; - void endArray(JsonArray array) throws IOException; - - void startObject(JsonObject object) throws IOException; - void visitObjectMember(JsonObject parent, String memberName, JsonPrimitive member, - boolean isFirst) throws IOException; - void visitObjectMember(JsonObject parent, String memberName, JsonArray member, - boolean isFirst) throws IOException; - void visitObjectMember(JsonObject parent, String memberName, JsonObject member, - boolean isFirst) throws IOException; - void visitNullObjectMember(JsonObject parent, String memberName, - boolean isFirst) throws IOException; - void endObject(JsonObject object) throws IOException; -} \ No newline at end of file diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonIOException.java b/src/com/massivecraft/mcore2/lib/gson/JsonIOException.java index 2dc26a33..3120cb86 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonIOException.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonIOException.java @@ -15,6 +15,8 @@ */ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.JsonParseException; + /** * This exception is raised when Gson was unable to read an input stream * or write to one. @@ -23,7 +25,6 @@ package com.massivecraft.mcore2.lib.gson; * @author Joel Leitch */ public final class JsonIOException extends JsonParseException { - private static final long serialVersionUID = 1L; public JsonIOException(String msg) { diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonNull.java b/src/com/massivecraft/mcore2/lib/gson/JsonNull.java index 8bcb0fd3..af6e73fd 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonNull.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonNull.java @@ -16,6 +16,9 @@ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonNull; + /** * A class representing a Json {@code null} value. * diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonObject.java b/src/com/massivecraft/mcore2/lib/gson/JsonObject.java index 9b26968c..1d69c144 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonObject.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonObject.java @@ -16,6 +16,11 @@ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.JsonArray; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonNull; +import com.massivecraft.mcore2.lib.gson.JsonObject; +import com.massivecraft.mcore2.lib.gson.JsonPrimitive; import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; import java.util.LinkedHashMap; diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonParseException.java b/src/com/massivecraft/mcore2/lib/gson/JsonParseException.java index 91925b57..0a521b44 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonParseException.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonParseException.java @@ -16,6 +16,8 @@ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.JsonParseException; + /** * This exception is raised if there is a serious issue that occurs during parsing of a Json * string. One of the main usages for this class is for the Gson infrastructure. If the incoming diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonParser.java b/src/com/massivecraft/mcore2/lib/gson/JsonParser.java index 58f6f87b..b9c39f59 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonParser.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonParser.java @@ -15,6 +15,11 @@ */ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonIOException; +import com.massivecraft.mcore2.lib.gson.JsonNull; +import com.massivecraft.mcore2.lib.gson.JsonParseException; +import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; import com.massivecraft.mcore2.lib.gson.internal.Streams; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; import com.massivecraft.mcore2.lib.gson.stream.JsonToken; diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonPrimitive.java b/src/com/massivecraft/mcore2/lib/gson/JsonPrimitive.java index eed6b08c..1747135c 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonPrimitive.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonPrimitive.java @@ -16,6 +16,8 @@ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonPrimitive; import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; import com.massivecraft.mcore2.lib.gson.internal.LazilyParsedNumber; diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonSerializationContext.java b/src/com/massivecraft/mcore2/lib/gson/JsonSerializationContext.java index 8c227e53..cbce7895 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonSerializationContext.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonSerializationContext.java @@ -18,6 +18,10 @@ package com.massivecraft.mcore2.lib.gson; import java.lang.reflect.Type; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonSerializationContext; +import com.massivecraft.mcore2.lib.gson.JsonSerializer; + /** * Context for serialization that is passed to a custom serializer during invocation of its * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonSerializer.java b/src/com/massivecraft/mcore2/lib/gson/JsonSerializer.java index 9ffaf8ab..ab0112ee 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonSerializer.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonSerializer.java @@ -18,6 +18,10 @@ package com.massivecraft.mcore2.lib.gson; import java.lang.reflect.Type; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonSerializationContext; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; + /** * Interface representing a custom serializer for Json. You should write a custom serializer, if * you are not happy with the default serialization done by Gson. You will also need to register @@ -60,6 +64,9 @@ import java.lang.reflect.Type; * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdSerializer()).create(); * * + *

    New applications should prefer {@link TypeAdapter}, whose streaming API + * is more efficient than this interface's tree API. + * * @author Inderjeet Singh * @author Joel Leitch * diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonStreamParser.java b/src/com/massivecraft/mcore2/lib/gson/JsonStreamParser.java index 8663a52c..b490c36c 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonStreamParser.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonStreamParser.java @@ -22,6 +22,10 @@ import java.io.StringReader; import java.util.Iterator; import java.util.NoSuchElementException; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonIOException; +import com.massivecraft.mcore2.lib.gson.JsonParseException; +import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; import com.massivecraft.mcore2.lib.gson.internal.Streams; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; import com.massivecraft.mcore2.lib.gson.stream.JsonToken; @@ -50,7 +54,6 @@ import com.massivecraft.mcore2.lib.gson.stream.MalformedJsonException; * @since 1.4 */ public final class JsonStreamParser implements Iterator { - private final JsonReader parser; private final Object lock; diff --git a/src/com/massivecraft/mcore2/lib/gson/JsonSyntaxException.java b/src/com/massivecraft/mcore2/lib/gson/JsonSyntaxException.java index 0ed9dc35..b5612cb8 100644 --- a/src/com/massivecraft/mcore2/lib/gson/JsonSyntaxException.java +++ b/src/com/massivecraft/mcore2/lib/gson/JsonSyntaxException.java @@ -15,6 +15,8 @@ */ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.JsonParseException; + /** * This exception is raised when Gson attempts to read (or write) a malformed * JSON element. diff --git a/src/com/massivecraft/mcore2/lib/gson/LongSerializationPolicy.java b/src/com/massivecraft/mcore2/lib/gson/LongSerializationPolicy.java index 00bbd1cb..9a8d4342 100644 --- a/src/com/massivecraft/mcore2/lib/gson/LongSerializationPolicy.java +++ b/src/com/massivecraft/mcore2/lib/gson/LongSerializationPolicy.java @@ -16,6 +16,9 @@ package com.massivecraft.mcore2.lib.gson; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonPrimitive; + /** * Defines the expected format for a {@code long} or {@code Long} type when its serialized. * @@ -31,44 +34,28 @@ public enum LongSerializationPolicy { * would be: * {@code {"f":123}}. */ - DEFAULT(new DefaultStrategy()), + DEFAULT() { + public JsonElement serialize(Long value) { + return new JsonPrimitive(value); + } + }, /** * Serializes a long value as a quoted string. For example, assume an object has a long field * named "f" then the serialized output would be: * {@code {"f":"123"}}. */ - STRING(new StringStrategy()); + STRING() { + public JsonElement serialize(Long value) { + return new JsonPrimitive(String.valueOf(value)); + } + }; - private final Strategy strategy; - - private LongSerializationPolicy(Strategy strategy) { - this.strategy = strategy; - } - /** * Serialize this {@code value} using this serialization policy. * * @param value the long value to be serialized into a {@link JsonElement} * @return the serialized version of {@code value} */ - public JsonElement serialize(Long value) { - return strategy.serialize(value); - } - - private interface Strategy { - JsonElement serialize(Long value); - } - - private static class DefaultStrategy implements Strategy { - public JsonElement serialize(Long value) { - return new JsonPrimitive(value); - } - } - - private static class StringStrategy implements Strategy { - public JsonElement serialize(Long value) { - return new JsonPrimitive(String.valueOf(value)); - } - } + public abstract JsonElement serialize(Long value); } diff --git a/src/com/massivecraft/mcore2/lib/gson/LowerCamelCaseSeparatorNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/LowerCamelCaseSeparatorNamingPolicy.java deleted file mode 100644 index 3742e624..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/LowerCamelCaseSeparatorNamingPolicy.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -/** - * A {@link FieldNamingStrategy2} that ensures the JSON field names consist of only - * lower case letters and are separated by a particular {@code separatorString}. - * - *

    The following is an example:

    - *
    - * class StringWrapper {
    - *   public String AStringField = "abcd";
    - * }
    - *
    - * LowerCamelCaseSeparatorNamingPolicy policy = new LowerCamelCaseSeparatorNamingPolicy("_");
    - * String translatedFieldName =
    - *     policy.translateName(StringWrapper.class.getField("AStringField"));
    - *
    - * assert("a_string_field".equals(translatedFieldName));
    - * 
    - * - * @author Joel Leitch - */ -final class LowerCamelCaseSeparatorNamingPolicy extends CompositionFieldNamingPolicy { - - public LowerCamelCaseSeparatorNamingPolicy(String separatorString) { - super(new CamelCaseSeparatorNamingPolicy(separatorString), new LowerCaseNamingPolicy()); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/LowerCaseNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/LowerCaseNamingPolicy.java deleted file mode 100644 index 0cd0ac77..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/LowerCaseNamingPolicy.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collection; - -/** - * A {@link FieldNamingStrategy2} that ensures the JSON field names consist of only - * lower case letters. - * - *

    The following is an example:

    - *
    - * class IntWrapper {
    - *   public int integerField = 0;
    - * }
    - *
    - * LowerCaseNamingPolicy policy = new LowerCaseNamingPolicy();
    - * String translatedFieldName =
    - *     policy.translateName(IntWrapper.class.getField("integerField"));
    - *
    - * assert("integerfield".equals(translatedFieldName));
    - * 
    - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class LowerCaseNamingPolicy extends RecursiveFieldNamingPolicy { - - @Override - protected String translateName(String target, Type fieldType, - Collection annotations) { - return target.toLowerCase(); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/LruCache.java b/src/com/massivecraft/mcore2/lib/gson/LruCache.java deleted file mode 100644 index 17b6e8aa..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/LruCache.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * An implementation of the {@link Cache} interface that evict objects from the cache using an - * LRU (least recently used) algorithm. Object start getting evicted from the cache once the - * {@code maxCapacity} is reached. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class LruCache extends LinkedHashMap implements Cache { - private static final long serialVersionUID = 1L; - - private final int maxCapacity; - - public LruCache(int maxCapacity) { - super(maxCapacity, 0.7F, true); - this.maxCapacity = maxCapacity; - } - - public synchronized void addElement(K key, V value) { - put(key, value); - } - - public synchronized V getElement(K key) { - return get(key); - } - - @Override - protected boolean removeEldestEntry(Map.Entry entry) { - return size() > maxCapacity; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/ModifierBasedExclusionStrategy.java b/src/com/massivecraft/mcore2/lib/gson/ModifierBasedExclusionStrategy.java deleted file mode 100644 index b69846fd..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/ModifierBasedExclusionStrategy.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import java.util.Collection; -import java.util.HashSet; - -/** - * Exclude fields based on particular field modifiers. For a list of possible - * modifiers, see {@link java.lang.reflect.Modifier}. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class ModifierBasedExclusionStrategy implements ExclusionStrategy { - private final Collection modifiers; - - public ModifierBasedExclusionStrategy(int... modifiers) { - this.modifiers = new HashSet(); - if (modifiers != null) { - for (int modifier : modifiers) { - this.modifiers.add(modifier); - } - } - } - - public boolean shouldSkipField(FieldAttributes f) { - for (int modifier : modifiers) { - if (f.hasModifier(modifier)) { - return true; - } - } - return false; - } - - public boolean shouldSkipClass(Class clazz) { - return false; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/ModifyFirstLetterNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/ModifyFirstLetterNamingPolicy.java deleted file mode 100644 index 371fd7a1..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/ModifyFirstLetterNamingPolicy.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collection; - -/** - * A {@link FieldNamingStrategy2} that ensures the JSON field names begins with - * an upper case letter. - * - *

    The following is an example:

    - *
    - * class StringWrapper {
    - *   public String stringField = "abcd";
    - *   public String _stringField = "efg";
    - * }
    - *
    - * ModifyFirstLetterNamingPolicy policy =
    - *     new ModifyFirstLetterNamingPolicy(LetterModifier.UPPER);
    - * String translatedFieldName =
    - *     policy.translateName(StringWrapper.class.getField("stringField"));
    - *
    - * assert("StringField".equals(translatedFieldName));
    - *
    - * String translatedFieldName =
    - *     policy.translateName(StringWrapper.class.getField("_stringField"));
    - *
    - * assert("_StringField".equals(translatedFieldName));
    - * 
    - * - * @author Joel Leitch - */ -final class ModifyFirstLetterNamingPolicy extends RecursiveFieldNamingPolicy { - - public enum LetterModifier { - UPPER, - LOWER; - } - - private final LetterModifier letterModifier; - - /** - * Creates a new ModifyFirstLetterNamingPolicy that will either modify the first letter of the - * target name to either UPPER case or LOWER case depending on the {@code modifier} parameter. - * - * @param modifier the type of modification that should be performed - * @throws IllegalArgumentException if {@code modifier} is null - */ - ModifyFirstLetterNamingPolicy(LetterModifier modifier) { - this.letterModifier = $Gson$Preconditions.checkNotNull(modifier); - } - - @Override - protected String translateName(String target, Type fieldType, - Collection annotations) { - StringBuilder fieldNameBuilder = new StringBuilder(); - int index = 0; - char firstCharacter = target.charAt(index); - - while (index < target.length() - 1) { - if (Character.isLetter(firstCharacter)) { - break; - } - - fieldNameBuilder.append(firstCharacter); - firstCharacter = target.charAt(++index); - } - - if (index == target.length()) { - return fieldNameBuilder.toString(); - } - - boolean capitalizeFirstLetter = (letterModifier == LetterModifier.UPPER); - if (capitalizeFirstLetter && !Character.isUpperCase(firstCharacter)) { - String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), target, ++index); - return fieldNameBuilder.append(modifiedTarget).toString(); - } else if (!capitalizeFirstLetter && Character.isUpperCase(firstCharacter)) { - String modifiedTarget = modifyString(Character.toLowerCase(firstCharacter), target, ++index); - return fieldNameBuilder.append(modifiedTarget).toString(); - } else { - return target; - } - } - - private String modifyString(char firstCharacter, String srcString, int indexOfSubstring) { - return (indexOfSubstring < srcString.length()) - ? firstCharacter + srcString.substring(indexOfSubstring) - : String.valueOf(firstCharacter); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/RecursiveFieldNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/RecursiveFieldNamingPolicy.java deleted file mode 100644 index 57681e2b..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/RecursiveFieldNamingPolicy.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collection; - -/** - * A mechanism for providing custom field naming in Gson. This allows the client code to translate - * field names into a particular convention that is not supported as a normal Java field - * declaration rules. For example, Java does not support "-" characters in a field name. - * - * @author Joel Leitch - */ -abstract class RecursiveFieldNamingPolicy implements FieldNamingStrategy2 { - - public final String translateName(FieldAttributes f) { - return translateName(f.getName(), f.getDeclaredType(), f.getAnnotations()); - } - - /** - * Performs the specific string translation. - * - * @param target the string object that will be manipulation/translated - * @param fieldType the actual type value of the field - * @param annotations the annotations set on the field - * @return the translated field name - */ - protected abstract String translateName(String target, Type fieldType, Collection annotations); -} diff --git a/src/com/massivecraft/mcore2/lib/gson/SerializedNameAnnotationInterceptingNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/SerializedNameAnnotationInterceptingNamingPolicy.java deleted file mode 100644 index 2fe72f20..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/SerializedNameAnnotationInterceptingNamingPolicy.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.annotations.SerializedName; - -/** - * A {@link FieldNamingStrategy2} that acts as a chain of responsibility. If the - * {@link com.massivecraft.mcore2.lib.gson.annotations.SerializedName} annotation is applied to a - * field then this strategy will translate the name to the {@code - * serializedName.value()}; otherwise it delegates to the wrapped - * {@link FieldNamingStrategy2}. - * - *

    - * NOTE: this class performs JSON field name validation for any of the fields - * marked with an {@code @SerializedName} annotation. - *

    - * - * @see SerializedName - * - * @author Joel Leitch - */ -final class SerializedNameAnnotationInterceptingNamingPolicy implements FieldNamingStrategy2 { - private final FieldNamingStrategy2 delegate; - - SerializedNameAnnotationInterceptingNamingPolicy(FieldNamingStrategy2 delegate) { - this.delegate = delegate; - } - - public String translateName(FieldAttributes f) { - SerializedName serializedName = f.getAnnotation(SerializedName.class); - return serializedName == null ? delegate.translateName(f) : serializedName.value(); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/SyntheticFieldExclusionStrategy.java b/src/com/massivecraft/mcore2/lib/gson/SyntheticFieldExclusionStrategy.java deleted file mode 100644 index 4e0b7a76..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/SyntheticFieldExclusionStrategy.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -/** - * A data object that stores attributes of a field. - * - *

    This class is immutable; therefore, it can be safely shared across threads. - * - * @author Inderjeet Singh - * @author Joel Leitch - * - * @since 1.4 - */ -final class SyntheticFieldExclusionStrategy implements ExclusionStrategy { - private final boolean skipSyntheticFields; - - SyntheticFieldExclusionStrategy(boolean skipSyntheticFields) { - this.skipSyntheticFields = skipSyntheticFields; - } - - public boolean shouldSkipClass(Class clazz) { - return false; - } - - public boolean shouldSkipField(FieldAttributes f) { - return skipSyntheticFields && f.isSynthetic(); - } - -} diff --git a/src/com/massivecraft/mcore2/lib/gson/TreeTypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/TreeTypeAdapter.java new file mode 100644 index 00000000..1ea928fa --- /dev/null +++ b/src/com/massivecraft/mcore2/lib/gson/TreeTypeAdapter.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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 com.massivecraft.mcore2.lib.gson; + +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.JsonDeserializer; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonSerializer; +import com.massivecraft.mcore2.lib.gson.TreeTypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; +import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; +import com.massivecraft.mcore2.lib.gson.internal.GsonInternalAccess; +import com.massivecraft.mcore2.lib.gson.internal.Streams; +import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; +import com.massivecraft.mcore2.lib.gson.stream.JsonReader; +import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; + +import java.io.IOException; + +/** + * Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the + * tree adapter may be serialization-only or deserialization-only, this class + * has a facility to lookup a delegate type adapter on demand. + */ +final class TreeTypeAdapter extends TypeAdapter { + private final JsonSerializer serializer; + private final JsonDeserializer deserializer; + private final Gson gson; + private final TypeToken typeToken; + private final TypeAdapterFactory skipPast; + + /** The delegate is lazily created because it may not be needed, and creating it may fail. */ + private TypeAdapter delegate; + + private TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deserializer, + Gson gson, TypeToken typeToken, TypeAdapterFactory skipPast) { + this.serializer = serializer; + this.deserializer = deserializer; + this.gson = gson; + this.typeToken = typeToken; + this.skipPast = skipPast; + } + + @Override public T read(JsonReader in) throws IOException { + if (deserializer == null) { + return delegate().read(in); + } + JsonElement value = Streams.parse(in); + if (value.isJsonNull()) { + return null; + } + return deserializer.deserialize(value, typeToken.getType(), gson.deserializationContext); + } + + @Override public void write(JsonWriter out, T value) throws IOException { + if (serializer == null) { + delegate().write(out, value); + return; + } + if (value == null) { + out.nullValue(); + return; + } + JsonElement tree = serializer.serialize(value, typeToken.getType(), gson.serializationContext); + Streams.write(tree, out); + } + + private TypeAdapter delegate() { + TypeAdapter d = delegate; + return d != null + ? d + : (delegate = GsonInternalAccess.INSTANCE.getNextAdapter(gson, skipPast, typeToken)); + } + + /** + * Returns a new factory that will match each type against {@code exactType}. + */ + public static TypeAdapterFactory newFactory(TypeToken exactType, Object typeAdapter) { + return new SingleTypeFactory(typeAdapter, exactType, false, null); + } + + /** + * Returns a new factory that will match each type and its raw type against + * {@code exactType}. + */ + public static TypeAdapterFactory newFactoryWithMatchRawType( + TypeToken exactType, Object typeAdapter) { + // only bother matching raw types if exact type is a raw type + boolean matchRawType = exactType.getType() == exactType.getRawType(); + return new SingleTypeFactory(typeAdapter, exactType, matchRawType, null); + } + + /** + * Returns a new factory that will match each type's raw type for assignability + * to {@code hierarchyType}. + */ + public static TypeAdapterFactory newTypeHierarchyFactory( + Class hierarchyType, Object typeAdapter) { + return new SingleTypeFactory(typeAdapter, null, false, hierarchyType); + } + + private static class SingleTypeFactory implements TypeAdapterFactory { + private final TypeToken exactType; + private final boolean matchRawType; + private final Class hierarchyType; + private final JsonSerializer serializer; + private final JsonDeserializer deserializer; + + private SingleTypeFactory(Object typeAdapter, TypeToken exactType, boolean matchRawType, + Class hierarchyType) { + serializer = typeAdapter instanceof JsonSerializer + ? (JsonSerializer) typeAdapter + : null; + deserializer = typeAdapter instanceof JsonDeserializer + ? (JsonDeserializer) typeAdapter + : null; + $Gson$Preconditions.checkArgument(serializer != null || deserializer != null); + this.exactType = exactType; + this.matchRawType = matchRawType; + this.hierarchyType = hierarchyType; + } + + @SuppressWarnings("unchecked") // guarded by typeToken.equals() call + public TypeAdapter create(Gson gson, TypeToken type) { + boolean matches = exactType != null + ? exactType.equals(type) || matchRawType && exactType.getType() == type.getRawType() + : hierarchyType.isAssignableFrom(type.getRawType()); + return matches + ? new TreeTypeAdapter((JsonSerializer) serializer, + (JsonDeserializer) deserializer, gson, type, this) + : null; + } + } +} diff --git a/src/com/massivecraft/mcore2/lib/gson/TypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/TypeAdapter.java new file mode 100644 index 00000000..804f29af --- /dev/null +++ b/src/com/massivecraft/mcore2/lib/gson/TypeAdapter.java @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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 com.massivecraft.mcore2.lib.gson; + +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.GsonBuilder; +import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonIOException; +import com.massivecraft.mcore2.lib.gson.JsonNull; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.internal.bind.JsonTreeReader; +import com.massivecraft.mcore2.lib.gson.internal.bind.JsonTreeWriter; +import com.massivecraft.mcore2.lib.gson.stream.JsonReader; +import com.massivecraft.mcore2.lib.gson.stream.JsonToken; +import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; + +/** + * Converts Java objects to and from JSON. + * + *

    Defining a type's JSON form

    + * By default Gson converts application classes to JSON using its built-in type + * adapters. If Gson's default JSON conversion isn't appropriate for a type, + * extend this class to customize the conversion. Here's an example of a type + * adapter for an (X,Y) coordinate point:
       {@code
    + *
    + *   public class PointAdapter extends TypeAdapter {
    + *     public Point read(JsonReader reader) throws IOException {
    + *       if (reader.peek() == JsonToken.NULL) {
    + *         reader.nextNull();
    + *         return null;
    + *       }
    + *       String xy = reader.nextString();
    + *       String[] parts = xy.split(",");
    + *       int x = Integer.parseInt(parts[0]);
    + *       int y = Integer.parseInt(parts[1]);
    + *       return new Point(x, y);
    + *     }
    + *     public void write(JsonWriter writer, Point value) throws IOException {
    + *       if (value == null) {
    + *         writer.nullValue();
    + *         return;
    + *       }
    + *       String xy = value.getX() + "," + value.getY();
    + *       writer.value(xy);
    + *     }
    + *   }}
    + * With this type adapter installed, Gson will convert {@code Points} to JSON as + * strings like {@code "5,8"} rather than objects like {@code {"x":5,"y":8}}. In + * this case the type adapter binds a rich Java class to a compact JSON value. + * + *

    The {@link #read(JsonReader) read()} method must read exactly one value + * and {@link #write(JsonWriter,Object) write()} must write exactly one value. + * For primitive types this is means readers should make exactly one call to + * {@code nextBoolean()}, {@code nextDouble()}, {@code nextInt()}, {@code + * nextLong()}, {@code nextString()} or {@code nextNull()}. Writers should make + * exactly one call to one of value() or nullValue(). + * For arrays, type adapters should start with a call to {@code beginArray()}, + * convert all elements, and finish with a call to {@code endArray()}. For + * objects, they should start with {@code beginObject()}, convert the object, + * and finish with {@code endObject()}. Failing to convert a value or converting + * too many values may cause the application to crash. + * + *

    Type adapters should be prepared to read null from the stream and write it + * to the stream. Alternatively, they should use {@link #nullSafe()} method while + * registering the type adapter with Gson. If your {@code Gson} instance + * has been configured to {@link GsonBuilder#serializeNulls()}, these nulls will be + * written to the final document. Otherwise the value (and the corresponding name + * when writing to a JSON object) will be omitted automatically. In either case + * your type adapter must handle null. + * + *

    To use a custom type adapter with Gson, you must register it with a + * {@link GsonBuilder}:

       {@code
    + *
    + *   GsonBuilder builder = new GsonBuilder();
    + *   builder.registerTypeAdapter(Point.class, new PointAdapter());
    + *   // if PointAdapter didn't check for nulls in its read/write methods, you should instead use
    + *   // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe());
    + *   ...
    + *   Gson gson = builder.create();
    + * }
    + * + * @since 2.1 + */ +// non-Javadoc: +// +//

    JSON Conversion

    +//

    A type adapter registered with Gson is automatically invoked while serializing +// or deserializing JSON. However, you can also use type adapters directly to serialize +// and deserialize JSON. Here is an example for deserialization:

       {@code
    +//
    +//   String json = "{'origin':'0,0','points':['1,2','3,4']}";
    +//   TypeAdapter graphAdapter = gson.getAdapter(Graph.class);
    +//   Graph graph = graphAdapter.fromJson(json);
    +// }
    +// And an example for serialization:
       {@code
    +//
    +//   Graph graph = new Graph(...);
    +//   TypeAdapter graphAdapter = gson.getAdapter(Graph.class);
    +//   String json = graphAdapter.toJson(graph);
    +// }
    +// +//

    Type adapters are type-specific. For example, a {@code +// TypeAdapter} can convert {@code Date} instances to JSON and JSON to +// instances of {@code Date}, but cannot convert any other types. +// +public abstract class TypeAdapter { + + /** + * Writes one JSON value (an array, object, string, number, boolean or null) + * for {@code value}. + * + * @param value the Java object to write. May be null. + */ + public abstract void write(JsonWriter out, T value) throws IOException; + + /** + * Converts {@code value} to a JSON document and writes it to {@code out}. + * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson} + * method, this write is strict. Create a {@link + * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call + * {@link #write(com.massivecraft.mcore2.lib.gson.stream.JsonWriter, Object)} for lenient + * writing. + * + * @param value the Java object to convert. May be null. + */ + /*public*/ final void toJson(Writer out, T value) throws IOException { + JsonWriter writer = new JsonWriter(out); + write(writer, value); + } + + /** + * This wrapper method is used to make a type adapter null tolerant. In general, a + * type adapter is required to handle nulls in write and read methods. Here is how this + * is typically done:
    + *

       {@code
    +   *
    +   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
    +   *   new TypeAdapter() {
    +   *     public Foo read(JsonReader in) throws IOException {
    +   *       if (in.peek() == JsonToken.NULL) {
    +   *         in.nextNull();
    +   *         return null;
    +   *       }
    +   *       // read a Foo from in and return it
    +   *     }
    +   *     public void write(JsonWriter out, Foo src) throws IOException {
    +   *       if (src == null) {
    +   *         out.nullValue();
    +   *         return;
    +   *       }
    +   *       // write src as JSON to out
    +   *     }
    +   *   }).create();
    +   * }
    + * You can avoid this boilerplate handling of nulls by wrapping your type adapter with + * this method. Here is how we will rewrite the above example: + *
       {@code
    +   *
    +   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
    +   *   new TypeAdapter() {
    +   *     public Foo read(JsonReader in) throws IOException {
    +   *       // read a Foo from in and return it
    +   *     }
    +   *     public void write(JsonWriter out, Foo src) throws IOException {
    +   *       // write src as JSON to out
    +   *     }
    +   *   }.nullSafe()).create();
    +   * }
    + * Note that we didn't need to check for nulls in our type adapter after we used nullSafe. + */ + public final TypeAdapter nullSafe() { + return new TypeAdapter() { + @Override public void write(JsonWriter out, T value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + TypeAdapter.this.write(out, value); + } + } + @Override public T read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + return TypeAdapter.this.read(reader); + } + }; + } + + /** + * Converts {@code value} to a JSON document. Unlike Gson's similar {@link + * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link + * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call + * {@link #write(com.massivecraft.mcore2.lib.gson.stream.JsonWriter, Object)} for lenient + * writing. + * + * @param value the Java object to convert. May be null. + */ + /*public*/ final String toJson(T value) throws IOException { + StringWriter stringWriter = new StringWriter(); + toJson(stringWriter, value); + return stringWriter.toString(); + } + + /** + * Converts {@code value} to a JSON tree. + * + * @param value the Java object to convert. May be null. + * @return the converted JSON tree. May be {@link JsonNull}. + */ + /*public*/ final JsonElement toJsonTree(T value) { + try { + JsonTreeWriter jsonWriter = new JsonTreeWriter(); + jsonWriter.setLenient(true); + write(jsonWriter, value); + return jsonWriter.get(); + } catch (IOException e) { + throw new JsonIOException(e); + } + } + + /** + * Reads one JSON value (an array, object, string, number, boolean or null) + * and converts it to a Java object. Returns the converted object. + * + * @return the converted Java object. May be null. + */ + public abstract T read(JsonReader in) throws IOException; + + /** + * Converts the JSON document in {@code in} to a Java object. Unlike Gson's + * similar {@link Gson#fromJson(java.io.Reader, Class) fromJson} method, this + * read is strict. Create a {@link JsonReader#setLenient(boolean) lenient} + * {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading. + * + * @return the converted Java object. May be null. + */ + /*public*/ final T fromJson(Reader in) throws IOException { + JsonReader reader = new JsonReader(in); + reader.setLenient(true); // TODO: non-lenient? + return read(reader); + } + + /** + * Converts the JSON document in {@code json} to a Java object. Unlike Gson's + * similar {@link Gson#fromJson(String, Class) fromJson} method, this read is + * strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code + * JsonReader} and call {@link #read(JsonReader)} for lenient reading. + * + * @return the converted Java object. May be null. + */ + /*public*/ final T fromJson(String json) throws IOException { + return fromJson(new StringReader(json)); + } + + /** + * Converts {@code jsonTree} to a Java object. + * + * @param jsonTree the Java object to convert. May be {@link JsonNull}. + */ + /*public*/ final T fromJsonTree(JsonElement jsonTree) { + try { + JsonReader jsonReader = new JsonTreeReader(jsonTree); + jsonReader.setLenient(true); + return read(jsonReader); + } catch (IOException e) { + throw new JsonIOException(e); + } + } +} diff --git a/src/com/massivecraft/mcore2/lib/gson/TypeAdapterFactory.java b/src/com/massivecraft/mcore2/lib/gson/TypeAdapterFactory.java new file mode 100644 index 00000000..3c3478db --- /dev/null +++ b/src/com/massivecraft/mcore2/lib/gson/TypeAdapterFactory.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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 com.massivecraft.mcore2.lib.gson; + +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; + +/** + * Creates type adapters for set of related types. Type adapter factories are + * most useful when several types share similar structure in their JSON form. + * + *

    Example: Converting enums to lowercase

    + * In this example, we implement a factory that creates type adapters for all + * enums. The type adapters will write enums in lowercase, despite the fact + * that they're defined in {@code CONSTANT_CASE} in the corresponding Java + * model:
       {@code
    + *
    + *   public class LowercaseEnumTypeAdapterFactory implements TypeAdapter.Factory {
    + *     public  TypeAdapter create(Gson gson, TypeToken type) {
    + *       Class rawType = (Class) type.getRawType();
    + *       if (!rawType.isEnum()) {
    + *         return null;
    + *       }
    + *
    + *       final Map lowercaseToConstant = new HashMap();
    + *       for (T constant : rawType.getEnumConstants()) {
    + *         lowercaseToConstant.put(toLowercase(constant), constant);
    + *       }
    + *
    + *       return new TypeAdapter() {
    + *         public void write(JsonWriter out, T value) throws IOException {
    + *           if (value == null) {
    + *             out.nullValue();
    + *           } else {
    + *             out.value(toLowercase(value));
    + *           }
    + *         }
    + *
    + *         public T read(JsonReader reader) throws IOException {
    + *           if (reader.peek() == JsonToken.NULL) {
    + *             reader.nextNull();
    + *             return null;
    + *           } else {
    + *             return lowercaseToConstant.get(reader.nextString());
    + *           }
    + *         }
    + *       };
    + *     }
    + *
    + *     private String toLowercase(Object o) {
    + *       return o.toString().toLowerCase(Locale.US);
    + *     }
    + *   }
    + * }
    + * + *

    Type adapter factories select which types they provide type adapters + * for. If a factory cannot support a given type, it must return null when + * that type is passed to {@link #create}. Factories should expect {@code + * create()} to be called on them for many types and should return null for + * most of those types. In the above example the factory returns null for + * calls to {@code create()} where {@code type} is not an enum. + * + *

    A factory is typically called once per type, but the returned type + * adapter may be used many times. It is most efficient to do expensive work + * like reflection in {@code create()} so that the type adapter's {@code + * read()} and {@code write()} methods can be very fast. In this example the + * mapping from lowercase name to enum value is computed eagerly. + * + *

    As with type adapters, factories must be registered with a {@link + * com.massivecraft.mcore2.lib.gson.GsonBuilder} for them to take effect:

       {@code
    + *
    + *  GsonBuilder builder = new GsonBuilder();
    + *  builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
    + *  ...
    + *  Gson gson = builder.create();
    + * }
    + * If multiple factories support the same type, the factory registered earlier + * takes precedence. + * + *

    Example: composing other type adapters

    + * In this example we implement a factory for Guava's {@code Multiset} + * collection type. The factory can be used to create type adapters for + * multisets of any element type: the type adapter for {@code + * Multiset} is different from the type adapter for {@code + * Multiset}. + * + *

    The type adapter delegates to another type adapter for the + * multiset elements. It figures out the element type by reflecting on the + * multiset's type token. A {@code Gson} is passed in to {@code create} for + * just this purpose:

       {@code
    + *
    + *   public class MultisetTypeAdapterFactory implements TypeAdapter.Factory {
    + *     public  TypeAdapter create(Gson gson, TypeToken typeToken) {
    + *       Type type = typeToken.getType();
    + *       if (typeToken.getRawType() != Multiset.class
    + *           || !(type instanceof ParameterizedType)) {
    + *         return null;
    + *       }
    + *
    + *       Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
    + *       TypeAdapter elementAdapter = gson.getAdapter(TypeToken.get(elementType));
    + *       return (TypeAdapter) newMultisetAdapter(elementAdapter);
    + *     }
    + *
    + *     private  TypeAdapter> newMultisetAdapter(
    + *         final TypeAdapter elementAdapter) {
    + *       return new TypeAdapter>() {
    + *         public void write(JsonWriter out, Multiset value) throws IOException {
    + *           if (value == null) {
    + *             out.nullValue();
    + *             return;
    + *           }
    + *
    + *           out.beginArray();
    + *           for (Multiset.Entry entry : value.entrySet()) {
    + *             out.value(entry.getCount());
    + *             elementAdapter.write(out, entry.getElement());
    + *           }
    + *           out.endArray();
    + *         }
    + *
    + *         public Multiset read(JsonReader in) throws IOException {
    + *           if (in.peek() == JsonToken.NULL) {
    + *             in.nextNull();
    + *             return null;
    + *           }
    + *
    + *           Multiset result = LinkedHashMultiset.create();
    + *           in.beginArray();
    + *           while (in.hasNext()) {
    + *             int count = in.nextInt();
    + *             E element = elementAdapter.read(in);
    + *             result.add(element, count);
    + *           }
    + *           in.endArray();
    + *           return result;
    + *         }
    + *       };
    + *     }
    + *   }
    + * }
    + * Delegating from one type adapter to another is extremely powerful; it's + * the foundation of how Gson converts Java objects and collections. Whenever + * possible your factory should retrieve its delegate type adapter in the + * {@code create()} method; this ensures potentially-expensive type adapter + * creation happens only once. + * + * @since 2.1 + */ +public interface TypeAdapterFactory { + + /** + * Returns a type adapter for {@code type}, or null if this factory doesn't + * support {@code type}. + */ + TypeAdapter create(Gson gson, TypeToken type); +} diff --git a/src/com/massivecraft/mcore2/lib/gson/UpperCamelCaseSeparatorNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/UpperCamelCaseSeparatorNamingPolicy.java deleted file mode 100644 index c336dc6f..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/UpperCamelCaseSeparatorNamingPolicy.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -/** - * A {@link FieldNamingStrategy2} that ensures the JSON field names consist of mixed - * case letters starting with a capital and are separated by a particular - * {@code separatorString}. - * - *

    The following is an example:

    - *
    - * class StringWrapper {
    - *   public String AStringField = "abcd";
    - * }
    - *
    - * UpperCamelCaseSeparatorNamingPolicy policy = new UpperCamelCaseSeparatorNamingPolicy("_");
    - * String translatedFieldName =
    - *     policy.translateName(StringWrapper.class.getField("AStringField"));
    - *
    - * assert("A_String_Field".equals(translatedFieldName));
    - * 
    - * - * @author Joel Leitch - */ -final class UpperCamelCaseSeparatorNamingPolicy extends CompositionFieldNamingPolicy { - - public UpperCamelCaseSeparatorNamingPolicy(String separatorString) { - super(new CamelCaseSeparatorNamingPolicy(separatorString), - new ModifyFirstLetterNamingPolicy(ModifyFirstLetterNamingPolicy.LetterModifier.UPPER)); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/UpperCaseNamingPolicy.java b/src/com/massivecraft/mcore2/lib/gson/UpperCaseNamingPolicy.java deleted file mode 100644 index de6fa63c..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/UpperCaseNamingPolicy.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collection; - -/** - * A {@link FieldNamingStrategy2} that ensures the JSON field names consist of only - * upper case letters. - * - *

    The following is an example:

    - *
    - * class IntWrapper {
    - *   public int integerField = 0;
    - * }
    - *
    - * UpperCaseNamingPolicy policy = new UpperCaseNamingPolicy();
    - * String translatedFieldName =
    - *     policy.translateName(IntWrapper.class.getField("integerField"));
    - *
    - * assert("INTEGERFIELD".equals(translatedFieldName));
    - * 
    - * - * @author Joel Leitch - */ -final class UpperCaseNamingPolicy extends RecursiveFieldNamingPolicy { - - @Override - protected String translateName(String target, Type fieldType, Collection annotations) { - return target.toUpperCase(); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/VersionConstants.java b/src/com/massivecraft/mcore2/lib/gson/VersionConstants.java deleted file mode 100644 index 5c0b1932..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/VersionConstants.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -/** - * Class contain all constants for versioning support. - * - * @author Joel Leitch - */ -final class VersionConstants { - // Prevent instantiation - private VersionConstants() { } - - static final double IGNORE_VERSIONS = -1D; -} diff --git a/src/com/massivecraft/mcore2/lib/gson/VersionExclusionStrategy.java b/src/com/massivecraft/mcore2/lib/gson/VersionExclusionStrategy.java deleted file mode 100644 index 9dcc910c..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/VersionExclusionStrategy.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson; - -import com.massivecraft.mcore2.lib.gson.annotations.Since; -import com.massivecraft.mcore2.lib.gson.annotations.Until; -import com.massivecraft.mcore2.lib.gson.internal.$Gson$Preconditions; - -/** - * This strategy will exclude any files and/or class that are passed the - * {@link #version} value. - * - * @author Joel Leitch - */ -final class VersionExclusionStrategy implements ExclusionStrategy { - private final double version; - - VersionExclusionStrategy(double version) { - $Gson$Preconditions.checkArgument(version >= 0.0D); - this.version = version; - } - - public boolean shouldSkipField(FieldAttributes f) { - return !isValidVersion(f.getAnnotation(Since.class), f.getAnnotation(Until.class)); - } - - public boolean shouldSkipClass(Class clazz) { - return !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class)); - } - - private boolean isValidVersion(Since since, Until until) { - return (isValidSince(since) && isValidUntil(until)); - } - - private boolean isValidSince(Since annotation) { - if (annotation != null) { - double annotationVersion = annotation.value(); - if (annotationVersion > version) { - return false; - } - } - return true; - } - - private boolean isValidUntil(Until annotation) { - if (annotation != null) { - double annotationVersion = annotation.value(); - if (annotationVersion <= version) { - return false; - } - } - return true; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/$Gson$Preconditions.java b/src/com/massivecraft/mcore2/lib/gson/internal/$Gson$Preconditions.java index e95bcef4..e61474ab 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/$Gson$Preconditions.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/$Gson$Preconditions.java @@ -42,10 +42,4 @@ public final class $Gson$Preconditions { throw new IllegalArgumentException(); } } - - public static void checkState(boolean condition) { - if (!condition) { - throw new IllegalStateException(); - } - } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/$Gson$Types.java b/src/com/massivecraft/mcore2/lib/gson/internal/$Gson$Types.java index f25c87ec..f0b7ba7c 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/$Gson$Types.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/$Gson$Types.java @@ -269,14 +269,6 @@ public final class $Gson$Types { $Gson$Types.getGenericSupertype(context, contextRawType, supertype)); } - /** - * Returns true if this type is an array. - */ - public static boolean isArray(Type type) { - return type instanceof GenericArrayType - || (type instanceof Class && ((Class) type).isArray()); - } - /** * Returns the component type of this array type. * @throws ClassCastException if this type is not an array. diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/ConstructorConstructor.java b/src/com/massivecraft/mcore2/lib/gson/internal/ConstructorConstructor.java index 18237b55..57d3c34b 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/ConstructorConstructor.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/ConstructorConstructor.java @@ -16,6 +16,8 @@ package com.massivecraft.mcore2.lib.gson.internal; +import com.massivecraft.mcore2.lib.gson.internal.ObjectConstructor; +import com.massivecraft.mcore2.lib.gson.internal.UnsafeAllocator; import com.massivecraft.mcore2.lib.gson.InstanceCreator; import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; @@ -24,6 +26,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -37,14 +40,14 @@ import java.util.TreeSet; * Returns a function that can construct an instance of a requested type. */ public final class ConstructorConstructor { - private final ParameterizedTypeHandlerMap> instanceCreators; + private final Map> instanceCreators; - public ConstructorConstructor(ParameterizedTypeHandlerMap> instanceCreators) { + public ConstructorConstructor(Map> instanceCreators) { this.instanceCreators = instanceCreators; } public ConstructorConstructor() { - this(new ParameterizedTypeHandlerMap>()); + this(Collections.>emptyMap()); } public ObjectConstructor getConstructor(TypeToken typeToken) { @@ -54,8 +57,7 @@ public final class ConstructorConstructor { // first try an instance creator @SuppressWarnings("unchecked") // types must agree - final InstanceCreator creator - = (InstanceCreator) instanceCreators.getHandlerFor(type, false); + final InstanceCreator creator = (InstanceCreator) instanceCreators.get(type); if (creator != null) { return new ObjectConstructor() { public T construct() { diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/Excluder.java b/src/com/massivecraft/mcore2/lib/gson/internal/Excluder.java new file mode 100644 index 00000000..9006b939 --- /dev/null +++ b/src/com/massivecraft/mcore2/lib/gson/internal/Excluder.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * 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 com.massivecraft.mcore2.lib.gson.internal; + +import com.massivecraft.mcore2.lib.gson.internal.Excluder; +import com.massivecraft.mcore2.lib.gson.internal.GsonInternalAccess; +import com.massivecraft.mcore2.lib.gson.ExclusionStrategy; +import com.massivecraft.mcore2.lib.gson.FieldAttributes; +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; +import com.massivecraft.mcore2.lib.gson.annotations.Expose; +import com.massivecraft.mcore2.lib.gson.annotations.Since; +import com.massivecraft.mcore2.lib.gson.annotations.Until; +import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; +import com.massivecraft.mcore2.lib.gson.stream.JsonReader; +import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * This class selects which fields and types to omit. It is configurable, + * supporting version attributes {@link Since} and {@link Until}, modifiers, + * synthetic fields, anonymous and local classes, inner classes, and fields with + * the {@link Expose} annotation. + * + *

    This class is a type adapter factory; types that are excluded will be + * adapted to null. It may delegate to another type adapter if only one + * direction is excluded. + * + * @author Joel Leitch + * @author Jesse Wilson + */ +public final class Excluder implements TypeAdapterFactory, Cloneable { + private static final double IGNORE_VERSIONS = -1.0d; + public static final Excluder DEFAULT = new Excluder(); + + private double version = IGNORE_VERSIONS; + private int modifiers = Modifier.TRANSIENT | Modifier.STATIC; + private boolean serializeInnerClasses = true; + private boolean requireExpose; + private List serializationStrategies = Collections.emptyList(); + private List deserializationStrategies = Collections.emptyList(); + + @Override protected Excluder clone() { + try { + return (Excluder) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + + public Excluder withVersion(double ignoreVersionsAfter) { + Excluder result = clone(); + result.version = ignoreVersionsAfter; + return result; + } + + public Excluder withModifiers(int... modifiers) { + Excluder result = clone(); + result.modifiers = 0; + for (int modifier : modifiers) { + result.modifiers |= modifier; + } + return result; + } + + public Excluder disableInnerClassSerialization() { + Excluder result = clone(); + result.serializeInnerClasses = false; + return result; + } + + public Excluder excludeFieldsWithoutExposeAnnotation() { + Excluder result = clone(); + result.requireExpose = true; + return result; + } + + public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy, + boolean serialization, boolean deserialization) { + Excluder result = clone(); + if (serialization) { + result.serializationStrategies = new ArrayList(serializationStrategies); + result.serializationStrategies.add(exclusionStrategy); + } + if (deserialization) { + result.deserializationStrategies + = new ArrayList(deserializationStrategies); + result.deserializationStrategies.add(exclusionStrategy); + } + return result; + } + + public TypeAdapter create(final Gson gson, final TypeToken type) { + Class rawType = type.getRawType(); + final boolean skipSerialize = excludeClass(rawType, true); + final boolean skipDeserialize = excludeClass(rawType, false); + + if (!skipSerialize && !skipDeserialize) { + return null; + } + + return new TypeAdapter() { + /** The delegate is lazily created because it may not be needed, and creating it may fail. */ + private TypeAdapter delegate; + + @Override public T read(JsonReader in) throws IOException { + if (skipDeserialize) { + in.skipValue(); + return null; + } + return delegate().read(in); + } + + @Override public void write(JsonWriter out, T value) throws IOException { + if (skipSerialize) { + out.nullValue(); + return; + } + delegate().write(out, value); + } + + private TypeAdapter delegate() { + TypeAdapter d = delegate; + return d != null + ? d + : (delegate = GsonInternalAccess.INSTANCE.getNextAdapter(gson, Excluder.this, type)); + } + }; + } + + public boolean excludeField(Field field, boolean serialize) { + if ((modifiers & field.getModifiers()) != 0) { + return true; + } + + if (version != Excluder.IGNORE_VERSIONS + && !isValidVersion(field.getAnnotation(Since.class), field.getAnnotation(Until.class))) { + return true; + } + + if (field.isSynthetic()) { + return true; + } + + if (requireExpose) { + Expose annotation = field.getAnnotation(Expose.class); + if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) { + return true; + } + } + + if (!serializeInnerClasses && isInnerClass(field.getType())) { + return true; + } + + if (isAnonymousOrLocal(field.getType())) { + return true; + } + + List list = serialize ? serializationStrategies : deserializationStrategies; + if (!list.isEmpty()) { + FieldAttributes fieldAttributes = new FieldAttributes(field); + for (ExclusionStrategy exclusionStrategy : list) { + if (exclusionStrategy.shouldSkipField(fieldAttributes)) { + return true; + } + } + } + + return false; + } + + public boolean excludeClass(Class clazz, boolean serialize) { + if (version != Excluder.IGNORE_VERSIONS + && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) { + return true; + } + + if (!serializeInnerClasses && isInnerClass(clazz)) { + return true; + } + + if (isAnonymousOrLocal(clazz)) { + return true; + } + + List list = serialize ? serializationStrategies : deserializationStrategies; + for (ExclusionStrategy exclusionStrategy : list) { + if (exclusionStrategy.shouldSkipClass(clazz)) { + return true; + } + } + + return false; + } + + private boolean isAnonymousOrLocal(Class clazz) { + return !Enum.class.isAssignableFrom(clazz) + && (clazz.isAnonymousClass() || clazz.isLocalClass()); + } + + private boolean isInnerClass(Class clazz) { + return clazz.isMemberClass() && !isStatic(clazz); + } + + private boolean isStatic(Class clazz) { + return (clazz.getModifiers() & Modifier.STATIC) != 0; + } + + private boolean isValidVersion(Since since, Until until) { + return isValidSince(since) && isValidUntil(until); + } + + private boolean isValidSince(Since annotation) { + if (annotation != null) { + double annotationVersion = annotation.value(); + if (annotationVersion > version) { + return false; + } + } + return true; + } + + private boolean isValidUntil(Until annotation) { + if (annotation != null) { + double annotationVersion = annotation.value(); + if (annotationVersion <= version) { + return false; + } + } + return true; + } +} diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/GsonInternalAccess.java b/src/com/massivecraft/mcore2/lib/gson/internal/GsonInternalAccess.java new file mode 100644 index 00000000..6cdc4f83 --- /dev/null +++ b/src/com/massivecraft/mcore2/lib/gson/internal/GsonInternalAccess.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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 com.massivecraft.mcore2.lib.gson.internal; + +import com.massivecraft.mcore2.lib.gson.internal.GsonInternalAccess; +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; +import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; + +/** + * Internal-only APIs of Gson available only to other classes in Gson. + */ +public abstract class GsonInternalAccess { + public static GsonInternalAccess INSTANCE; + + /** + * Returns a type adapter for {@code} type that isn't {@code skipPast}. This + * can be used for type adapters to compose other, simpler type adapters. + * + * @throws IllegalArgumentException if this GSON cannot serialize and + * deserialize {@code type}. + */ + public abstract TypeAdapter getNextAdapter( + Gson gson, TypeAdapterFactory skipPast, TypeToken type); +} diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/JsonReaderInternalAccess.java b/src/com/massivecraft/mcore2/lib/gson/internal/JsonReaderInternalAccess.java new file mode 100644 index 00000000..84dda13b --- /dev/null +++ b/src/com/massivecraft/mcore2/lib/gson/internal/JsonReaderInternalAccess.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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 com.massivecraft.mcore2.lib.gson.internal; + +import com.massivecraft.mcore2.lib.gson.internal.JsonReaderInternalAccess; +import com.massivecraft.mcore2.lib.gson.stream.JsonReader; + +import java.io.IOException; + +/** + * Internal-only APIs of JsonReader available only to other classes in Gson. + */ +public abstract class JsonReaderInternalAccess { + public static JsonReaderInternalAccess INSTANCE; + + /** + * Changes the type of the current property name token to a string value. + */ + public abstract void promoteNameToValue(JsonReader reader) throws IOException; +} diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/Pair.java b/src/com/massivecraft/mcore2/lib/gson/internal/Pair.java index 2a876119..f677e53f 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/Pair.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/Pair.java @@ -16,6 +16,8 @@ package com.massivecraft.mcore2.lib.gson.internal; +import com.massivecraft.mcore2.lib.gson.internal.Pair; + /** * A simple object that holds onto a pair of object references, first and second. * diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/ParameterizedTypeHandlerMap.java b/src/com/massivecraft/mcore2/lib/gson/internal/ParameterizedTypeHandlerMap.java deleted file mode 100644 index 256f9453..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/internal/ParameterizedTypeHandlerMap.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson.internal; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A map that provides ability to associate handlers for a specific type or all - * of its sub-types - * - * @author Inderjeet Singh - * @author Joel Leitch - * - * @param The handler that will be looked up by type - */ -public final class ParameterizedTypeHandlerMap { - - private static final Logger logger = - Logger.getLogger(ParameterizedTypeHandlerMap.class.getName()); - /** - * Map that is meant for storing default type adapters - */ - private final Map systemMap = new HashMap(); - private final Map userMap = new HashMap(); - /** - * List of default type hierarchy adapters - */ - private final List, T>> systemTypeHierarchyList = new ArrayList, T>>(); - private final List, T>> userTypeHierarchyList = new ArrayList, T>>(); - private boolean modifiable = true; - - public synchronized void registerForTypeHierarchy(Class typeOfT, T value, boolean isSystem) { - Pair, T> pair = new Pair, T>(typeOfT, value); - registerForTypeHierarchy(pair, isSystem); - } - - public synchronized void registerForTypeHierarchy(Pair, T> pair, boolean isSystem) { - if (!modifiable) { - throw new IllegalStateException("Attempted to modify an unmodifiable map."); - } - List, T>> typeHierarchyList = isSystem ? systemTypeHierarchyList : userTypeHierarchyList; - int index = getIndexOfSpecificHandlerForTypeHierarchy(pair.first, typeHierarchyList); - if (index >= 0) { - logger.log(Level.WARNING, "Overriding the existing type handler for {0}", pair.first); - typeHierarchyList.remove(index); - } - index = getIndexOfAnOverriddenHandler(pair.first, typeHierarchyList); - if (index >= 0) { - throw new IllegalArgumentException("The specified type handler for type " + pair.first - + " hides the previously registered type hierarchy handler for " - + typeHierarchyList.get(index).first + ". Gson does not allow this."); - } - // We want stack behavior for adding to this list. A type adapter added subsequently should - // override a previously registered one. - typeHierarchyList.add(0, pair); - } - - private static int getIndexOfAnOverriddenHandler(Class type, List, T>> typeHierarchyList) { - for (int i = typeHierarchyList.size()-1; i >= 0; --i) { - Pair, T> entry = typeHierarchyList.get(i); - if (type.isAssignableFrom(entry.first)) { - return i; - } - } - return -1; - } - - public synchronized void register(Type typeOfT, T value, boolean isSystem) { - if (!modifiable) { - throw new IllegalStateException("Attempted to modify an unmodifiable map."); - } - if (hasSpecificHandlerFor(typeOfT)) { - logger.log(Level.WARNING, "Overriding the existing type handler for {0}", typeOfT); - } - Map map = isSystem ? systemMap : userMap; - map.put(typeOfT, value); - } - - public synchronized void registerIfAbsent(ParameterizedTypeHandlerMap other) { - if (!modifiable) { - throw new IllegalStateException("Attempted to modify an unmodifiable map."); - } - for (Map.Entry entry : other.userMap.entrySet()) { - if (!userMap.containsKey(entry.getKey())) { - register(entry.getKey(), entry.getValue(), false); - } - } - for (Map.Entry entry : other.systemMap.entrySet()) { - if (!systemMap.containsKey(entry.getKey())) { - register(entry.getKey(), entry.getValue(), true); - } - } - // Quite important to traverse the typeHierarchyList from stack bottom first since - // we want to register the handlers in the same order to preserve priority order - for (int i = other.userTypeHierarchyList.size()-1; i >= 0; --i) { - Pair, T> entry = other.userTypeHierarchyList.get(i); - int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first, userTypeHierarchyList); - if (index < 0) { - registerForTypeHierarchy(entry, false); - } - } - for (int i = other.systemTypeHierarchyList.size()-1; i >= 0; --i) { - Pair, T> entry = other.systemTypeHierarchyList.get(i); - int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first, systemTypeHierarchyList); - if (index < 0) { - registerForTypeHierarchy(entry, true); - } - } - } - - public synchronized ParameterizedTypeHandlerMap makeUnmodifiable() { - modifiable = false; - return this; - } - - public synchronized T getHandlerFor(Type type, boolean systemOnly) { - T handler; - if (!systemOnly) { - handler = userMap.get(type); - if (handler != null) { - return handler; - } - } - handler = systemMap.get(type); - if (handler != null) { - return handler; - } - Class rawClass = $Gson$Types.getRawType(type); - if (rawClass != type) { - handler = getHandlerFor(rawClass, systemOnly); - if (handler != null) { - return handler; - } - } - // check if something registered for type hierarchy - handler = getHandlerForTypeHierarchy(rawClass, systemOnly); - return handler; - } - - private T getHandlerForTypeHierarchy(Class type, boolean systemOnly) { - if (!systemOnly) { - for (Pair, T> entry : userTypeHierarchyList) { - if (entry.first.isAssignableFrom(type)) { - return entry.second; - } - } - } - for (Pair, T> entry : systemTypeHierarchyList) { - if (entry.first.isAssignableFrom(type)) { - return entry.second; - } - } - return null; - } - - public synchronized boolean hasSpecificHandlerFor(Type type) { - return userMap.containsKey(type) || systemMap.containsKey(type); - } - - private static int getIndexOfSpecificHandlerForTypeHierarchy( - Class type, List, T>> typeHierarchyList) { - for (int i = typeHierarchyList.size()-1; i >= 0; --i) { - if (type.equals(typeHierarchyList.get(i).first)) { - return i; - } - } - return -1; - } - - public synchronized ParameterizedTypeHandlerMap copyOf() { - ParameterizedTypeHandlerMap copy = new ParameterizedTypeHandlerMap(); - // Instead of individually registering entries in the map, make an efficient copy - // of the list and map - - // TODO (inder): Performance optimization. We can probably just share the - // systemMap and systemTypeHierarchyList instead of making copies - copy.systemMap.putAll(systemMap); - copy.userMap.putAll(userMap); - copy.systemTypeHierarchyList.addAll(systemTypeHierarchyList); - copy.userTypeHierarchyList.addAll(userTypeHierarchyList); - return copy; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("{userTypeHierarchyList:{"); - appendList(sb, userTypeHierarchyList); - sb.append("},systemTypeHierarchyList:{"); - appendList(sb, systemTypeHierarchyList); - sb.append("},userMap:{"); - appendMap(sb, userMap); - sb.append("},systemMap:{"); - appendMap(sb, systemMap); - sb.append("}"); - return sb.toString(); - } - - private void appendList(StringBuilder sb, List,T>> list) { - boolean first = true; - for (Pair, T> entry : list) { - if (first) { - first = false; - } else { - sb.append(','); - } - sb.append(typeToString(entry.first)).append(':'); - sb.append(entry.second); - } - } - - private void appendMap(StringBuilder sb, Map map) { - boolean first = true; - for (Map.Entry entry : map.entrySet()) { - if (first) { - first = false; - } else { - sb.append(','); - } - sb.append(typeToString(entry.getKey())).append(':'); - sb.append(entry.getValue()); - } - } - - private String typeToString(Type type) { - return $Gson$Types.getRawType(type).getSimpleName(); - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/UnsafeAllocator.java b/src/com/massivecraft/mcore2/lib/gson/internal/UnsafeAllocator.java index aafc15b1..fa7ec894 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/UnsafeAllocator.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/UnsafeAllocator.java @@ -21,6 +21,8 @@ import java.io.ObjectStreamClass; import java.lang.reflect.Field; import java.lang.reflect.Method; +import com.massivecraft.mcore2.lib.gson.internal.UnsafeAllocator; + /** * Do sneaky things to allocate objects without invoking their constructors. * diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/ArrayTypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/ArrayTypeAdapter.java index 7f797241..40938e83 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/ArrayTypeAdapter.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/ArrayTypeAdapter.java @@ -23,6 +23,11 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import com.massivecraft.mcore2.lib.gson.internal.bind.ArrayTypeAdapter; +import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapterRuntimeTypeWrapper; +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.internal.$Gson$Types; import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; @@ -33,45 +38,43 @@ import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; * Adapt an array of objects. */ public final class ArrayTypeAdapter extends TypeAdapter { - public static final Factory FACTORY = new Factory() { + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @SuppressWarnings({"unchecked", "rawtypes"}) - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson gson, TypeToken typeToken) { Type type = typeToken.getType(); if (!(type instanceof GenericArrayType || type instanceof Class && ((Class) type).isArray())) { return null; } Type componentType = $Gson$Types.getArrayComponentType(type); - TypeAdapter componentTypeAdapter = context.getAdapter(TypeToken.get(componentType)); - // create() doesn't define a type parameter - TypeAdapter result = new ArrayTypeAdapter( - context, componentTypeAdapter, $Gson$Types.getRawType(componentType)); - return result; + TypeAdapter componentTypeAdapter = gson.getAdapter(TypeToken.get(componentType)); + return new ArrayTypeAdapter( + gson, componentTypeAdapter, $Gson$Types.getRawType(componentType)); } }; private final Class componentType; private final TypeAdapter componentTypeAdapter; - public ArrayTypeAdapter(MiniGson context, TypeAdapter componentTypeAdapter, Class componentType) { + public ArrayTypeAdapter(Gson context, TypeAdapter componentTypeAdapter, Class componentType) { this.componentTypeAdapter = new TypeAdapterRuntimeTypeWrapper(context, componentTypeAdapter, componentType); this.componentType = componentType; } - public Object read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + public Object read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } List list = new ArrayList(); - reader.beginArray(); - while (reader.hasNext()) { - E instance = componentTypeAdapter.read(reader); + in.beginArray(); + while (in.hasNext()) { + E instance = componentTypeAdapter.read(in); list.add(instance); } - reader.endArray(); + in.endArray(); Object array = Array.newInstance(componentType, list.size()); for (int i = 0; i < list.size(); i++) { Array.set(array, i, list.get(i)); @@ -80,17 +83,17 @@ public final class ArrayTypeAdapter extends TypeAdapter { } @SuppressWarnings("unchecked") - @Override public void write(JsonWriter writer, Object array) throws IOException { + @Override public void write(JsonWriter out, Object array) throws IOException { if (array == null) { - writer.nullValue(); // TODO: better policy here? + out.nullValue(); return; } - writer.beginArray(); + out.beginArray(); for (int i = 0, length = Array.getLength(array); i < length; i++) { - final E value = (E) Array.get(array, i); - componentTypeAdapter.write(writer, value); + E value = (E) Array.get(array, i); + componentTypeAdapter.write(out, value); } - writer.endArray(); + out.endArray(); } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/BigDecimalTypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/BigDecimalTypeAdapter.java index 9bc891f4..494aead2 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/BigDecimalTypeAdapter.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/BigDecimalTypeAdapter.java @@ -17,6 +17,7 @@ package com.massivecraft.mcore2.lib.gson.internal.bind; import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; import com.massivecraft.mcore2.lib.gson.stream.JsonToken; import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; @@ -32,20 +33,20 @@ import java.math.BigDecimal; public final class BigDecimalTypeAdapter extends TypeAdapter { @Override - public BigDecimal read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + public BigDecimal read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } try { - return new BigDecimal(reader.nextString()); + return new BigDecimal(in.nextString()); } catch (NumberFormatException e) { throw new JsonSyntaxException(e); } } @Override - public void write(JsonWriter writer, BigDecimal value) throws IOException { - writer.value(value); + public void write(JsonWriter out, BigDecimal value) throws IOException { + out.value(value); } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/BigIntegerTypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/BigIntegerTypeAdapter.java index 0bd4fc6a..225c4e87 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/BigIntegerTypeAdapter.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/BigIntegerTypeAdapter.java @@ -17,6 +17,7 @@ package com.massivecraft.mcore2.lib.gson.internal.bind; import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; import com.massivecraft.mcore2.lib.gson.stream.JsonToken; import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; @@ -32,20 +33,20 @@ import java.math.BigInteger; public final class BigIntegerTypeAdapter extends TypeAdapter { @Override - public BigInteger read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + public BigInteger read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } try { - return new BigInteger(reader.nextString()); + return new BigInteger(in.nextString()); } catch (NumberFormatException e) { throw new JsonSyntaxException(e); } } @Override - public void write(JsonWriter writer, BigInteger value) throws IOException { - writer.value(value); + public void write(JsonWriter out, BigInteger value) throws IOException { + out.value(value); } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/CollectionTypeAdapterFactory.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/CollectionTypeAdapterFactory.java index 4d51a357..25b13410 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/CollectionTypeAdapterFactory.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/CollectionTypeAdapterFactory.java @@ -16,6 +16,10 @@ package com.massivecraft.mcore2.lib.gson.internal.bind; +import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapterRuntimeTypeWrapper; +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.internal.$Gson$Types; import com.massivecraft.mcore2.lib.gson.internal.ConstructorConstructor; import com.massivecraft.mcore2.lib.gson.internal.ObjectConstructor; @@ -31,14 +35,14 @@ import java.util.Collection; /** * Adapt a homogeneous collection of objects. */ -public final class CollectionTypeAdapterFactory implements TypeAdapter.Factory { +public final class CollectionTypeAdapterFactory implements TypeAdapterFactory { private final ConstructorConstructor constructorConstructor; public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) { this.constructorConstructor = constructorConstructor; } - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson gson, TypeToken typeToken) { Type type = typeToken.getType(); Class rawType = typeToken.getRawType(); @@ -47,11 +51,11 @@ public final class CollectionTypeAdapterFactory implements TypeAdapter.Factory { } Type elementType = $Gson$Types.getCollectionElementType(type, rawType); - TypeAdapter elementTypeAdapter = context.getAdapter(TypeToken.get(elementType)); + TypeAdapter elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType)); ObjectConstructor constructor = constructorConstructor.getConstructor(typeToken); @SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter - TypeAdapter result = new Adapter(context, elementType, elementTypeAdapter, constructor); + TypeAdapter result = new Adapter(gson, elementType, elementTypeAdapter, constructor); return result; } @@ -59,7 +63,7 @@ public final class CollectionTypeAdapterFactory implements TypeAdapter.Factory { private final TypeAdapter elementTypeAdapter; private final ObjectConstructor> constructor; - public Adapter(MiniGson context, Type elementType, + public Adapter(Gson context, Type elementType, TypeAdapter elementTypeAdapter, ObjectConstructor> constructor) { this.elementTypeAdapter = @@ -67,33 +71,33 @@ public final class CollectionTypeAdapterFactory implements TypeAdapter.Factory { this.constructor = constructor; } - public Collection read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + public Collection read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } Collection collection = constructor.construct(); - reader.beginArray(); - while (reader.hasNext()) { - E instance = elementTypeAdapter.read(reader); + in.beginArray(); + while (in.hasNext()) { + E instance = elementTypeAdapter.read(in); collection.add(instance); } - reader.endArray(); + in.endArray(); return collection; } - public void write(JsonWriter writer, Collection collection) throws IOException { + public void write(JsonWriter out, Collection collection) throws IOException { if (collection == null) { - writer.nullValue(); // TODO: better policy here? + out.nullValue(); // TODO: better policy here? return; } - writer.beginArray(); + out.beginArray(); for (E element : collection) { - elementTypeAdapter.write(writer, element); + elementTypeAdapter.write(out, element); } - writer.endArray(); + out.endArray(); } } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/DateTypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/DateTypeAdapter.java index 548fcae5..9d1161c7 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/DateTypeAdapter.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/DateTypeAdapter.java @@ -16,7 +16,11 @@ package com.massivecraft.mcore2.lib.gson.internal.bind; +import com.massivecraft.mcore2.lib.gson.internal.bind.DateTypeAdapter; +import com.massivecraft.mcore2.lib.gson.Gson; import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; import com.massivecraft.mcore2.lib.gson.stream.JsonToken; @@ -37,9 +41,9 @@ import java.util.TimeZone; * to synchronize its read and write methods. */ public final class DateTypeAdapter extends TypeAdapter { - public static final Factory FACTORY = new Factory() { + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson gson, TypeToken typeToken) { return typeToken.getRawType() == Date.class ? (TypeAdapter) new DateTypeAdapter() : null; } }; @@ -56,12 +60,12 @@ public final class DateTypeAdapter extends TypeAdapter { return iso8601Format; } - @Override public Date read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + @Override public Date read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } - return deserializeToDate(reader.nextString()); + return deserializeToDate(in.nextString()); } private synchronized Date deserializeToDate(String json) { @@ -80,12 +84,12 @@ public final class DateTypeAdapter extends TypeAdapter { } } - @Override public synchronized void write(JsonWriter writer, Date value) throws IOException { + @Override public synchronized void write(JsonWriter out, Date value) throws IOException { if (value == null) { - writer.nullValue(); + out.nullValue(); return; } String dateFormatAsString = enUsFormat.format(value); - writer.value(dateFormatAsString); + out.value(dateFormatAsString); } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/ExcludedTypeAdapterFactory.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/ExcludedTypeAdapterFactory.java deleted file mode 100644 index a788363d..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/ExcludedTypeAdapterFactory.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson.internal.bind; - -import com.massivecraft.mcore2.lib.gson.ExclusionStrategy; -import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; -import com.massivecraft.mcore2.lib.gson.stream.JsonReader; -import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; - -import java.io.IOException; - -/** - * This type adapter skips values using an exclusion strategy. It may delegate - * to another type adapter if only one direction is excluded. - */ -public final class ExcludedTypeAdapterFactory implements TypeAdapter.Factory { - private final ExclusionStrategy serializationExclusionStrategy; - private final ExclusionStrategy deserializationExclusionStrategy; - - public ExcludedTypeAdapterFactory(ExclusionStrategy serializationExclusionStrategy, - ExclusionStrategy deserializationExclusionStrategy) { - this.serializationExclusionStrategy = serializationExclusionStrategy; - this.deserializationExclusionStrategy = deserializationExclusionStrategy; - } - - public TypeAdapter create(final MiniGson context, final TypeToken type) { - Class rawType = type.getRawType(); - final boolean skipSerialize = serializationExclusionStrategy.shouldSkipClass(rawType); - final boolean skipDeserialize = deserializationExclusionStrategy.shouldSkipClass(rawType); - - if (!skipSerialize && !skipDeserialize) { - return null; - } - - return new TypeAdapter() { - /** - * The delegate is lazily created because it may not be needed, and - * creating it may fail. - */ - private TypeAdapter delegate; - - @Override public T read(JsonReader reader) throws IOException { - if (skipDeserialize) { - reader.skipValue(); - return null; - } - return delegate().read(reader); - } - - @Override public void write(JsonWriter writer, T value) throws IOException { - if (skipSerialize) { - writer.nullValue(); - return; - } - delegate().write(writer, value); - } - - private TypeAdapter delegate() { - TypeAdapter d = delegate; - return d != null - ? d - : (delegate = context.getNextAdapter(ExcludedTypeAdapterFactory.this, type)); - } - }; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonElementReader.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonTreeReader.java similarity index 94% rename from src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonElementReader.java rename to src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonTreeReader.java index 285e7737..df5346a8 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonElementReader.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonTreeReader.java @@ -37,7 +37,7 @@ import java.util.Map; * * @author Jesse Wilson */ -public final class JsonElementReader extends JsonReader { +public final class JsonTreeReader extends JsonReader { private static final Reader UNREADABLE_READER = new Reader() { @Override public int read(char[] buffer, int offset, int count) throws IOException { throw new AssertionError(); @@ -50,7 +50,7 @@ public final class JsonElementReader extends JsonReader { private final List stack = new ArrayList(); - public JsonElementReader(JsonElement element) { + public JsonTreeReader(JsonElement element) { super(UNREADABLE_READER); stack.add(element); } @@ -216,4 +216,12 @@ public final class JsonElementReader extends JsonReader { @Override public String toString() { return getClass().getSimpleName(); } + + public void promoteNameToValue() throws IOException { + expect(JsonToken.NAME); + Iterator i = (Iterator) peekStack(); + Map.Entry entry = (Map.Entry) i.next(); + stack.add(entry.getValue()); + stack.add(new JsonPrimitive((String)entry.getKey())); + } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonElementWriter.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonTreeWriter.java similarity index 98% rename from src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonElementWriter.java rename to src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonTreeWriter.java index 9039ed7b..3f50507e 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonElementWriter.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/JsonTreeWriter.java @@ -31,7 +31,7 @@ import java.util.List; /** * This writer creates a JsonElement. */ -public final class JsonElementWriter extends JsonWriter { +public final class JsonTreeWriter extends JsonWriter { private static final Writer UNWRITABLE_WRITER = new Writer() { @Override public void write(char[] buffer, int offset, int counter) { throw new AssertionError(); @@ -55,7 +55,7 @@ public final class JsonElementWriter extends JsonWriter { /** the JSON element constructed by this writer. */ private JsonElement product = JsonNull.INSTANCE; // TODO: is this really what we want?; - public JsonElementWriter() { + public JsonTreeWriter() { super(UNWRITABLE_WRITER); } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/MapTypeAdapterFactory.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/MapTypeAdapterFactory.java index ea8afea1..6cbbd0f3 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/MapTypeAdapterFactory.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/MapTypeAdapterFactory.java @@ -16,11 +16,19 @@ package com.massivecraft.mcore2.lib.gson.internal.bind; +import com.massivecraft.mcore2.lib.gson.internal.bind.JsonTreeWriter; +import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapterRuntimeTypeWrapper; +import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapters; +import com.massivecraft.mcore2.lib.gson.Gson; import com.massivecraft.mcore2.lib.gson.JsonElement; +import com.massivecraft.mcore2.lib.gson.JsonIOException; import com.massivecraft.mcore2.lib.gson.JsonPrimitive; import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.internal.$Gson$Types; import com.massivecraft.mcore2.lib.gson.internal.ConstructorConstructor; +import com.massivecraft.mcore2.lib.gson.internal.JsonReaderInternalAccess; import com.massivecraft.mcore2.lib.gson.internal.ObjectConstructor; import com.massivecraft.mcore2.lib.gson.internal.Streams; import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; @@ -57,9 +65,9 @@ import java.util.Map; * But GSON is unable to deserialize this value because the JSON string name is * just the {@link Object#toString() toString()} of the map key. Attempting to * convert the above JSON to an object fails with a parse exception: - *
    com.google.gson.JsonParseException: Expecting object found: "(5,6)"
    - *   at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
    - *   at com.google.gson.ObjectNavigator.navigateClassFields
    + * 
    com.massivecraft.mcore2.lib.gson.JsonParseException: Expecting object found: "(5,6)"
    + *   at com.massivecraft.mcore2.lib.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
    + *   at com.massivecraft.mcore2.lib.gson.ObjectNavigator.navigateClassFields
      *   ...
    * *

    Maps as JSON arrays

    @@ -99,7 +107,7 @@ import java.util.Map; * This format will serialize and deserialize just fine as long as this adapter * is registered. */ -public final class MapTypeAdapterFactory implements TypeAdapter.Factory { +public final class MapTypeAdapterFactory implements TypeAdapterFactory { private final ConstructorConstructor constructorConstructor; private final boolean complexMapKeySerialization; @@ -109,7 +117,7 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { this.complexMapKeySerialization = complexMapKeySerialization; } - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson gson, TypeToken typeToken) { Type type = typeToken.getType(); Class rawType = typeToken.getRawType(); @@ -119,13 +127,13 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { Class rawTypeOfSrc = $Gson$Types.getRawType(type); Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc); - TypeAdapter keyAdapter = getKeyAdapter(context, keyAndValueTypes[0]); - TypeAdapter valueAdapter = context.getAdapter(TypeToken.get(keyAndValueTypes[1])); + TypeAdapter keyAdapter = getKeyAdapter(gson, keyAndValueTypes[0]); + TypeAdapter valueAdapter = gson.getAdapter(TypeToken.get(keyAndValueTypes[1])); ObjectConstructor constructor = constructorConstructor.getConstructor(typeToken); @SuppressWarnings({"unchecked", "rawtypes"}) // we don't define a type parameter for the key or value types - TypeAdapter result = new Adapter(context, keyAndValueTypes[0], keyAdapter, + TypeAdapter result = new Adapter(gson, keyAndValueTypes[0], keyAdapter, keyAndValueTypes[1], valueAdapter, constructor); return result; } @@ -133,7 +141,7 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { /** * Returns a type adapter that writes the value as a string. */ - private TypeAdapter getKeyAdapter(MiniGson context, Type keyType) { + private TypeAdapter getKeyAdapter(Gson context, Type keyType) { return (keyType == boolean.class || keyType == Boolean.class) ? TypeAdapters.BOOLEAN_AS_STRING : context.getAdapter(TypeToken.get(keyType)); @@ -144,7 +152,7 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { private final TypeAdapter valueTypeAdapter; private final ObjectConstructor> constructor; - public Adapter(MiniGson context, Type keyType, TypeAdapter keyTypeAdapter, + public Adapter(Gson context, Type keyType, TypeAdapter keyTypeAdapter, Type valueType, TypeAdapter valueTypeAdapter, ObjectConstructor> constructor) { this.keyTypeAdapter = @@ -154,57 +162,57 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { this.constructor = constructor; } - public Map read(JsonReader reader) throws IOException { - JsonToken peek = reader.peek(); + public Map read(JsonReader in) throws IOException { + JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { - reader.nextNull(); + in.nextNull(); return null; } Map map = constructor.construct(); if (peek == JsonToken.BEGIN_ARRAY) { - reader.beginArray(); - while (reader.hasNext()) { - reader.beginArray(); // entry array - K key = keyTypeAdapter.read(reader); - V value = valueTypeAdapter.read(reader); + in.beginArray(); + while (in.hasNext()) { + in.beginArray(); // entry array + K key = keyTypeAdapter.read(in); + V value = valueTypeAdapter.read(in); V replaced = map.put(key, value); if (replaced != null) { throw new JsonSyntaxException("duplicate key: " + key); } - reader.endArray(); + in.endArray(); } - reader.endArray(); + in.endArray(); } else { - reader.beginObject(); - while (reader.hasNext()) { - String keyString = reader.nextName(); - K key = keyTypeAdapter.fromJsonElement(new JsonPrimitive(keyString)); - V value = valueTypeAdapter.read(reader); + in.beginObject(); + while (in.hasNext()) { + JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in); + K key = keyTypeAdapter.read(in); + V value = valueTypeAdapter.read(in); V replaced = map.put(key, value); if (replaced != null) { throw new JsonSyntaxException("duplicate key: " + key); } } - reader.endObject(); + in.endObject(); } return map; } - public void write(JsonWriter writer, Map map) throws IOException { + public void write(JsonWriter out, Map map) throws IOException { if (map == null) { - writer.nullValue(); // TODO: better policy here? + out.nullValue(); return; } if (!complexMapKeySerialization) { - writer.beginObject(); + out.beginObject(); for (Map.Entry entry : map.entrySet()) { - writer.name(String.valueOf(entry.getKey())); - valueTypeAdapter.write(writer, entry.getValue()); + out.name(String.valueOf(entry.getKey())); + valueTypeAdapter.write(out, entry.getValue()); } - writer.endObject(); + out.endObject(); return; } @@ -213,29 +221,29 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { List values = new ArrayList(map.size()); for (Map.Entry entry : map.entrySet()) { - JsonElement keyElement = keyTypeAdapter.toJsonElement(entry.getKey()); + JsonElement keyElement = toJsonTree(keyTypeAdapter, entry.getKey()); keys.add(keyElement); values.add(entry.getValue()); hasComplexKeys |= keyElement.isJsonArray() || keyElement.isJsonObject(); } if (hasComplexKeys) { - writer.beginArray(); + out.beginArray(); for (int i = 0; i < keys.size(); i++) { - writer.beginArray(); // entry array - Streams.write(keys.get(i), writer); - valueTypeAdapter.write(writer, values.get(i)); - writer.endArray(); + out.beginArray(); // entry array + Streams.write(keys.get(i), out); + valueTypeAdapter.write(out, values.get(i)); + out.endArray(); } - writer.endArray(); + out.endArray(); } else { - writer.beginObject(); + out.beginObject(); for (int i = 0; i < keys.size(); i++) { JsonElement keyElement = keys.get(i); - writer.name(keyToString(keyElement)); - valueTypeAdapter.write(writer, values.get(i)); + out.name(keyToString(keyElement)); + valueTypeAdapter.write(out, values.get(i)); } - writer.endObject(); + out.endObject(); } } @@ -258,4 +266,16 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { } } } + + // TODO: remove this when TypeAdapter.toJsonTree() is public + private static JsonElement toJsonTree(TypeAdapter typeAdapter, T value) { + try { + JsonTreeWriter jsonWriter = new JsonTreeWriter(); + jsonWriter.setLenient(true); + typeAdapter.write(jsonWriter, value); + return jsonWriter.get(); + } catch (IOException e) { + throw new JsonIOException(e); + } + } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/MiniGson.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/MiniGson.java deleted file mode 100644 index b2d9aaea..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/MiniGson.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson.internal.bind; - -import com.massivecraft.mcore2.lib.gson.internal.ConstructorConstructor; -import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; -import com.massivecraft.mcore2.lib.gson.stream.JsonReader; -import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A basic binding between JSON and Java objects. - */ -public final class MiniGson { - /** - * This thread local guards against reentrant calls to getAdapter(). In - * certain object graphs, creating an adapter for a type may recursively - * require an adapter for the same type! Without intervention, the recursive - * lookup would stack overflow. We cheat by returning a proxy type adapter. - * The proxy is wired up once the initial adapter has been created. - */ - private final ThreadLocal, FutureTypeAdapter>> calls - = new ThreadLocal, FutureTypeAdapter>>() { - @Override protected Map, FutureTypeAdapter> initialValue() { - return new HashMap, FutureTypeAdapter>(); - } - }; - - private final List factories; - - private MiniGson(Builder builder) { - ConstructorConstructor constructorConstructor = new ConstructorConstructor(); - List factories = new ArrayList(); - if (builder.addDefaultFactories) { - factories.add(TypeAdapters.BOOLEAN_FACTORY); - factories.add(TypeAdapters.INTEGER_FACTORY); - factories.add(TypeAdapters.DOUBLE_FACTORY); - factories.add(TypeAdapters.FLOAT_FACTORY); - factories.add(TypeAdapters.LONG_FACTORY); - factories.add(TypeAdapters.STRING_FACTORY); - } - factories.addAll(builder.factories); - if (builder.addDefaultFactories) { - factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); - factories.add(new StringToValueMapTypeAdapterFactory(constructorConstructor)); - factories.add(ArrayTypeAdapter.FACTORY); - factories.add(ObjectTypeAdapter.FACTORY); - factories.add(new ReflectiveTypeAdapterFactory(constructorConstructor)); - } - this.factories = Collections.unmodifiableList(factories); - } - - /** - * Returns the type adapter for {@code} type. - * - * @throws IllegalArgumentException if this GSON cannot serialize and - * deserialize {@code type}. - */ - public TypeAdapter getAdapter(TypeToken type) { - // TODO: create a cache! - - Map, FutureTypeAdapter> threadCalls = calls.get(); - @SuppressWarnings("unchecked") // the key and value type parameters always agree - FutureTypeAdapter ongoingCall = (FutureTypeAdapter) threadCalls.get(type); - if (ongoingCall != null) { - return ongoingCall; - } - - FutureTypeAdapter call = new FutureTypeAdapter(); - threadCalls.put(type, call); - try { - for (TypeAdapter.Factory factory : factories) { - TypeAdapter candidate = factory.create(this, type); - if (candidate != null) { - call.setDelegate(candidate); - return candidate; - } - } - throw new IllegalArgumentException("This MiniGSON cannot handle " + type); - } finally { - threadCalls.remove(type); - } - } - - /** - * Returns a type adapter for {@code} type that isn't {@code skipPast}. This - * can be used for type adapters to compose other, simpler type adapters. - * - * @throws IllegalArgumentException if this GSON cannot serialize and - * deserialize {@code type}. - */ - public TypeAdapter getNextAdapter(TypeAdapter.Factory skipPast, TypeToken type) { - boolean skipPastFound = false; - - for (TypeAdapter.Factory factory : factories) { - if (!skipPastFound) { - if (factory == skipPast) { - skipPastFound = true; - } - continue; - } - - TypeAdapter candidate = factory.create(this, type); - if (candidate != null) { - return candidate; - } - } - - throw new IllegalArgumentException("This MiniGSON cannot serialize " + type); - } - - static class FutureTypeAdapter extends TypeAdapter { - private TypeAdapter delegate; - - public void setDelegate(TypeAdapter typeAdapter) { - if (delegate != null) { - throw new AssertionError(); - } - delegate = typeAdapter; - } - - @Override public T read(JsonReader reader) throws IOException { - if (delegate == null) { - throw new IllegalStateException(); - } - return delegate.read(reader); - } - - @Override public void write(JsonWriter writer, T value) throws IOException { - if (delegate == null) { - throw new IllegalStateException(); - } - delegate.write(writer, value); - } - } - - /** - * Returns the type adapter for {@code} type. - * - * @throws IllegalArgumentException if this GSON cannot serialize and - * deserialize {@code type}. - */ - public TypeAdapter getAdapter(Class type) { - return getAdapter(TypeToken.get(type)); - } - - /** - * Returns the type adapters of this context in order of precedence. - */ - public List getFactories() { - return factories; - } - - public static final class Builder { - private final List factories = new ArrayList(); - boolean addDefaultFactories = true; - - public Builder factory(TypeAdapter.Factory factory) { - factories.add(factory); - return this; - } - - public Builder withoutDefaultFactories() { - this.addDefaultFactories = false; - return this; - } - - public Builder typeAdapter(final Class type, final TypeAdapter typeAdapter) { - factories.add(TypeAdapters.newFactory(type, typeAdapter)); - return this; - } - - public Builder typeAdapter(TypeToken type, TypeAdapter typeAdapter) { - factories.add(TypeAdapters.newFactory(type, typeAdapter)); - return this; - } - - public Builder typeHierarchyAdapter(Class type, TypeAdapter typeAdapter) { - factories.add(TypeAdapters.newTypeHierarchyFactory(type, typeAdapter)); - return this; - } - - public MiniGson build() { - return new MiniGson(this); - } - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/ObjectTypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/ObjectTypeAdapter.java index 2e46633a..ab124267 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/ObjectTypeAdapter.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/ObjectTypeAdapter.java @@ -16,6 +16,10 @@ package com.massivecraft.mcore2.lib.gson.internal.bind; +import com.massivecraft.mcore2.lib.gson.internal.bind.ObjectTypeAdapter; +import com.massivecraft.mcore2.lib.gson.Gson; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; import com.massivecraft.mcore2.lib.gson.stream.JsonToken; @@ -32,54 +36,54 @@ import java.util.Map; * serialization and a primitive/Map/List on deserialization. */ public final class ObjectTypeAdapter extends TypeAdapter { - public static final Factory FACTORY = new Factory() { + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @SuppressWarnings("unchecked") - public TypeAdapter create(MiniGson context, TypeToken type) { + public TypeAdapter create(Gson gson, TypeToken type) { if (type.getRawType() == Object.class) { - return (TypeAdapter) new ObjectTypeAdapter(context); + return (TypeAdapter) new ObjectTypeAdapter(gson); } return null; } }; - private final MiniGson miniGson; + private final Gson gson; - private ObjectTypeAdapter(MiniGson miniGson) { - this.miniGson = miniGson; + private ObjectTypeAdapter(Gson gson) { + this.gson = gson; } - @Override public Object read(JsonReader reader) throws IOException { - JsonToken token = reader.peek(); + @Override public Object read(JsonReader in) throws IOException { + JsonToken token = in.peek(); switch (token) { case BEGIN_ARRAY: List list = new ArrayList(); - reader.beginArray(); - while (reader.hasNext()) { - list.add(read(reader)); + in.beginArray(); + while (in.hasNext()) { + list.add(read(in)); } - reader.endArray(); + in.endArray(); return list; case BEGIN_OBJECT: Map map = new LinkedHashMap(); - reader.beginObject(); - while (reader.hasNext()) { - map.put(reader.nextName(), read(reader)); + in.beginObject(); + while (in.hasNext()) { + map.put(in.nextName(), read(in)); } - reader.endObject(); + in.endObject(); return map; case STRING: - return reader.nextString(); + return in.nextString(); case NUMBER: - return reader.nextDouble(); + return in.nextDouble(); case BOOLEAN: - return reader.nextBoolean(); + return in.nextBoolean(); case NULL: - reader.nextNull(); + in.nextNull(); return null; } @@ -87,19 +91,19 @@ public final class ObjectTypeAdapter extends TypeAdapter { } @SuppressWarnings("unchecked") - @Override public void write(JsonWriter writer, Object value) throws IOException { + @Override public void write(JsonWriter out, Object value) throws IOException { if (value == null) { - writer.nullValue(); // TODO: better policy here? + out.nullValue(); return; } - TypeAdapter typeAdapter = (TypeAdapter) miniGson.getAdapter(value.getClass()); + TypeAdapter typeAdapter = (TypeAdapter) gson.getAdapter(value.getClass()); if (typeAdapter instanceof ObjectTypeAdapter) { - writer.beginObject(); - writer.endObject(); + out.beginObject(); + out.endObject(); return; } - typeAdapter.write(writer, value); + typeAdapter.write(out, value); } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/Reflection.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/Reflection.java deleted file mode 100644 index cdec2a6f..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/Reflection.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson.internal.bind; - -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; - -final class Reflection { - /** - * Finds a compatible runtime type if it is more specific - */ - public static Type getRuntimeTypeIfMoreSpecific(Type type, Object value) { - if (value != null - && (type == Object.class || type instanceof TypeVariable || type instanceof Class)) { - type = value.getClass(); - } - return type; - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/ReflectiveTypeAdapterFactory.java index 189fa16f..853f3c70 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/ReflectiveTypeAdapterFactory.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/ReflectiveTypeAdapterFactory.java @@ -16,9 +16,17 @@ package com.massivecraft.mcore2.lib.gson.internal.bind; +import com.massivecraft.mcore2.lib.gson.internal.bind.ReflectiveTypeAdapterFactory; +import com.massivecraft.mcore2.lib.gson.internal.bind.TypeAdapterRuntimeTypeWrapper; +import com.massivecraft.mcore2.lib.gson.FieldNamingStrategy; +import com.massivecraft.mcore2.lib.gson.Gson; import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; +import com.massivecraft.mcore2.lib.gson.annotations.SerializedName; import com.massivecraft.mcore2.lib.gson.internal.$Gson$Types; import com.massivecraft.mcore2.lib.gson.internal.ConstructorConstructor; +import com.massivecraft.mcore2.lib.gson.internal.Excluder; import com.massivecraft.mcore2.lib.gson.internal.ObjectConstructor; import com.massivecraft.mcore2.lib.gson.internal.Primitives; import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; @@ -27,7 +35,6 @@ import com.massivecraft.mcore2.lib.gson.stream.JsonToken; import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; import java.io.IOException; -import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.LinkedHashMap; @@ -36,26 +43,28 @@ import java.util.Map; /** * Type adapter that reflects over the fields and methods of a class. */ -public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { +public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { private final ConstructorConstructor constructorConstructor; + private final FieldNamingStrategy fieldNamingPolicy; + private final Excluder excluder; - public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor) { + public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor, + FieldNamingStrategy fieldNamingPolicy, Excluder excluder) { this.constructorConstructor = constructorConstructor; + this.fieldNamingPolicy = fieldNamingPolicy; + this.excluder = excluder; } - protected boolean serializeField(Class declaringClazz, Field f, Type declaredType) { - return !f.isSynthetic(); + public boolean excludeField(Field f, boolean serialize) { + return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize); } - protected boolean deserializeField(Class declaringClazz, Field f, Type declaredType) { - return !f.isSynthetic(); + private String getFieldName(Field f) { + SerializedName serializedName = f.getAnnotation(SerializedName.class); + return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value(); } - protected String getFieldName(Class declaringClazz, Field f, Type declaredType) { - return f.getName(); - } - - public TypeAdapter create(MiniGson context, final TypeToken type) { + public TypeAdapter create(Gson gson, final TypeToken type) { Class raw = type.getRawType(); if (!Object.class.isAssignableFrom(raw)) { @@ -63,11 +72,11 @@ public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { } ObjectConstructor constructor = constructorConstructor.getConstructor(type); - return new Adapter(constructor, getBoundFields(context, type, raw)); + return new Adapter(constructor, getBoundFields(gson, type, raw)); } private ReflectiveTypeAdapterFactory.BoundField createBoundField( - final MiniGson context, final Field field, final String name, + final Gson context, final Field field, final String name, final TypeToken fieldType, boolean serialize, boolean deserialize) { final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); @@ -92,8 +101,7 @@ public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { }; } - private Map getBoundFields( - MiniGson context, TypeToken type, Class raw) { + private Map getBoundFields(Gson context, TypeToken type, Class raw) { Map result = new LinkedHashMap(); if (raw.isInterface()) { return result; @@ -102,15 +110,15 @@ public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { Type declaredType = type.getType(); while (raw != Object.class) { Field[] fields = raw.getDeclaredFields(); - AccessibleObject.setAccessible(fields, true); for (Field field : fields) { - boolean serialize = serializeField(raw, field, declaredType); - boolean deserialize = deserializeField(raw, field, declaredType); + boolean serialize = excludeField(field, true); + boolean deserialize = excludeField(field, false); if (!serialize && !deserialize) { continue; } + field.setAccessible(true); Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); - BoundField boundField = createBoundField(context, field, getFieldName(raw, field, declaredType), + BoundField boundField = createBoundField(context, field, getFieldName(field), TypeToken.get(fieldType), serialize, deserialize); BoundField previous = result.put(boundField.name, boundField); if (previous != null) { @@ -149,9 +157,9 @@ public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { } @Override - public T read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + public T read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } @@ -160,15 +168,15 @@ public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { // TODO: null out the other fields? try { - reader.beginObject(); - while (reader.hasNext()) { - String name = reader.nextName(); + in.beginObject(); + while (in.hasNext()) { + String name = in.nextName(); BoundField field = boundFields.get(name); if (field == null || !field.deserialized) { // TODO: define a better policy - reader.skipValue(); + in.skipValue(); } else { - field.read(reader, instance); + field.read(in, instance); } } } catch (IllegalStateException e) { @@ -176,29 +184,29 @@ public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { } catch (IllegalAccessException e) { throw new AssertionError(e); } - reader.endObject(); + in.endObject(); return instance; } @Override - public void write(JsonWriter writer, T value) throws IOException { + public void write(JsonWriter out, T value) throws IOException { if (value == null) { - writer.nullValue(); // TODO: better policy here? + out.nullValue(); // TODO: better policy here? return; } - writer.beginObject(); + out.beginObject(); try { for (BoundField boundField : boundFields.values()) { if (boundField.serialized) { - writer.name(boundField.name); - boundField.write(writer, value); + out.name(boundField.name); + boundField.write(out, value); } } } catch (IllegalAccessException e) { throw new AssertionError(); } - writer.endObject(); + out.endObject(); } } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/SqlDateTypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/SqlDateTypeAdapter.java index 695ca7f5..2be7163a 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/SqlDateTypeAdapter.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/SqlDateTypeAdapter.java @@ -16,7 +16,11 @@ package com.massivecraft.mcore2.lib.gson.internal.bind; +import com.massivecraft.mcore2.lib.gson.internal.bind.SqlDateTypeAdapter; +import com.massivecraft.mcore2.lib.gson.Gson; import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; import com.massivecraft.mcore2.lib.gson.stream.JsonToken; @@ -34,9 +38,9 @@ import java.text.SimpleDateFormat; * to synchronize its read and write methods. */ public final class SqlDateTypeAdapter extends TypeAdapter { - public static final Factory FACTORY = new Factory() { + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson gson, TypeToken typeToken) { return typeToken.getRawType() == java.sql.Date.class ? (TypeAdapter) new SqlDateTypeAdapter() : null; } @@ -45,13 +49,13 @@ public final class SqlDateTypeAdapter extends TypeAdapter { private final DateFormat format = new SimpleDateFormat("MMM d, yyyy"); @Override - public synchronized java.sql.Date read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); + public synchronized java.sql.Date read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); return null; } try { - final long utilDate = format.parse(reader.nextString()).getTime(); + final long utilDate = format.parse(in.nextString()).getTime(); return new java.sql.Date(utilDate); } catch (ParseException e) { throw new JsonSyntaxException(e); @@ -59,7 +63,7 @@ public final class SqlDateTypeAdapter extends TypeAdapter { } @Override - public synchronized void write(JsonWriter writer, java.sql.Date value) throws IOException { - writer.value(value == null ? null : format.format(value)); + public synchronized void write(JsonWriter out, java.sql.Date value) throws IOException { + out.value(value == null ? null : format.format(value)); } } diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/StringToValueMapTypeAdapterFactory.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/StringToValueMapTypeAdapterFactory.java deleted file mode 100644 index 1a43fe3b..00000000 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/StringToValueMapTypeAdapterFactory.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * 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 com.massivecraft.mcore2.lib.gson.internal.bind; - -import com.massivecraft.mcore2.lib.gson.internal.$Gson$Types; -import com.massivecraft.mcore2.lib.gson.internal.ConstructorConstructor; -import com.massivecraft.mcore2.lib.gson.internal.ObjectConstructor; -import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; -import com.massivecraft.mcore2.lib.gson.stream.JsonReader; -import com.massivecraft.mcore2.lib.gson.stream.JsonToken; -import com.massivecraft.mcore2.lib.gson.stream.JsonWriter; - -import java.io.IOException; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Map; - -/** - * Adapt a map whose keys are strings. - */ -public final class StringToValueMapTypeAdapterFactory implements TypeAdapter.Factory { - private final ConstructorConstructor constructorConstructor; - - public StringToValueMapTypeAdapterFactory(ConstructorConstructor constructorConstructor) { - this.constructorConstructor = constructorConstructor; - } - - public TypeAdapter create(MiniGson context, TypeToken typeToken) { - Type type = typeToken.getType(); - if (!(type instanceof ParameterizedType)) { - return null; - } - - Class rawType = typeToken.getRawType(); - if (!Map.class.isAssignableFrom(rawType)) { - return null; - } - - Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawType); - if (keyAndValueTypes[0] != String.class) { - return null; - } - TypeAdapter valueAdapter = context.getAdapter(TypeToken.get(keyAndValueTypes[1])); - - ObjectConstructor constructor = constructorConstructor.getConstructor(typeToken); - - @SuppressWarnings({"unchecked", "rawtypes"}) - // we don't define a type parameter for the key or value types - TypeAdapter result = new Adapter(valueAdapter, constructor); - return result; - } - - private final class Adapter extends TypeAdapter> { - private final TypeAdapter valueTypeAdapter; - private final ObjectConstructor> constructor; - - public Adapter(TypeAdapter valueTypeAdapter, - ObjectConstructor> constructor) { - this.valueTypeAdapter = valueTypeAdapter; - this.constructor = constructor; - } - - public Map read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); - return null; - } - - Map map = constructor.construct(); - reader.beginObject(); - while (reader.hasNext()) { - String key = reader.nextName(); - V value = valueTypeAdapter.read(reader); - map.put(key, value); - } - reader.endObject(); - return map; - } - - public void write(JsonWriter writer, Map map) throws IOException { - if (map == null) { - writer.nullValue(); // TODO: better policy here? - return; - } - - writer.beginObject(); - for (Map.Entry entry : map.entrySet()) { - writer.name(entry.getKey()); - valueTypeAdapter.write(writer, entry.getValue()); - } - writer.endObject(); - } - } -} diff --git a/src/com/massivecraft/mcore2/lib/gson/internal/bind/TimeTypeAdapter.java b/src/com/massivecraft/mcore2/lib/gson/internal/bind/TimeTypeAdapter.java index f0c56cc9..b0e65efe 100644 --- a/src/com/massivecraft/mcore2/lib/gson/internal/bind/TimeTypeAdapter.java +++ b/src/com/massivecraft/mcore2/lib/gson/internal/bind/TimeTypeAdapter.java @@ -16,7 +16,11 @@ package com.massivecraft.mcore2.lib.gson.internal.bind; +import com.massivecraft.mcore2.lib.gson.internal.bind.TimeTypeAdapter; +import com.massivecraft.mcore2.lib.gson.Gson; import com.massivecraft.mcore2.lib.gson.JsonSyntaxException; +import com.massivecraft.mcore2.lib.gson.TypeAdapter; +import com.massivecraft.mcore2.lib.gson.TypeAdapterFactory; import com.massivecraft.mcore2.lib.gson.reflect.TypeToken; import com.massivecraft.mcore2.lib.gson.stream.JsonReader; import com.massivecraft.mcore2.lib.gson.stream.JsonToken; @@ -36,29 +40,29 @@ import java.util.Date; * to synchronize its read and write methods. */ public final class TimeTypeAdapter extends TypeAdapter