Package com.google.wave.api.event

Source Code of com.google.wave.api.event.EventSerializer$KeyConverter

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.google.wave.api.event;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.wave.api.Wavelet;
import com.google.wave.api.JsonRpcConstant.ParamsProperty;
import com.google.wave.api.impl.EventMessageBundle;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
* Object that is responsible for serializing and deserializing implementors
* of {@link Event} to and from {@link JsonObject}.
*/
public class EventSerializer {

  /**
   * An interface for map key conversion.
   *
   * @param <T> the generic type of the resulting key.
   */
  private static interface KeyConverter<T> {

    /**
     * Converts the given string key into an instance of {@code T}.
     *
     * @param key the key to be converted.
     * @return an instance of {@code T} that represents the given string key.
     */
    T convert(String key);
  }

  /** Constants for accessing the properties of the given JSON object. */
  private static final String TYPE = "type";
  private static final String MODIFIED_BY = "modifiedBy";
  private static final String TIMESTAMP = "timestamp";
  private static final String BLIP_ID = "blipId";
  private static final String WAVELET = "wavelet";
  private static final String PROPERTIES = "properties";
  private static final String BUNDLE = "bundle";

  /**
   * Serializes the given {@link Event} into a {@link JsonObject}.
   *
   * @param event the {@link Event} to be serialized.
   * @param context the serialization context.
   * @return an instance of {@link JsonObject}, that is the JSON representation
   *     of the given {@link Event}.
   */
  public static JsonObject serialize(Event event, JsonSerializationContext context)
      throws EventSerializationException {
    JsonObject result = new JsonObject();

    // Serialize basic properties from Event.
    result.addProperty(TYPE, event.getType().name());
    result.addProperty(MODIFIED_BY, event.getModifiedBy());
    result.addProperty(TIMESTAMP, event.getTimestamp());
    result.addProperty(TIMESTAMP, event.getTimestamp());

    // Construct a properties object.
    JsonObject properties = new JsonObject();

    try {
      // Serialize the blip id.
      Field blipIdField = AbstractEvent.class.getDeclaredField(BLIP_ID);
      blipIdField.setAccessible(true);
      properties.addProperty(BLIP_ID, (String) blipIdField.get(event));

      // Serialize event specific properties.
      for (Field field : event.getClass().getDeclaredFields()) {
        field.setAccessible(true);
        properties.add(field.getName(), context.serialize(field.get(event)));
      }
    } catch (IllegalArgumentException e) {
      throw new EventSerializationException("Unable to serialize event: " + BLIP_ID +
          " in " +  event.getClass() + " is not accessible.");
    } catch (IllegalAccessException e) {
      throw new EventSerializationException("Unable to serialize event: " + BLIP_ID +
          " in " +  event.getClass() + " is not accessible.");
    } catch (NoSuchFieldException e) {
      throw new EventSerializationException("Unable to serialize event: " + BLIP_ID +
          " in " +  event.getClass() + " is not accessible.");
    }

    result.add(PROPERTIES, properties);
    return result;
  }

  /**
   * Deserializes the given {@link JsonObject} into an {@link Event}, and
   * assign the given {@link Wavelet} to the {@link Event}.
   *
   * @param wavelet the wavelet where the event occurred.
   * @param json the JSON representation of {@link Event}.
   * @param context the deserialization context.
   * @return an instance of {@link Event}.
   *
   * @throw {@link EventSerializationException} if there is a problem
   *     deserializing the event JSON.
   */
  public static Event deserialize(Wavelet wavelet, EventMessageBundle bundle, JsonObject json,
      JsonDeserializationContext context) throws EventSerializationException {
    // Construct the event object.
    String eventTypeString = json.get(TYPE).getAsString();
    EventType type = EventType.valueOfIgnoreCase(eventTypeString);
    if (type == EventType.UNKNOWN) {
      throw new EventSerializationException("Trying to deserialize event JSON with unknown " +
          "type: " + json, json);
    }

    // Parse the generic parameters.
    String modifiedBy = json.get(MODIFIED_BY).getAsString();
    Long timestamp = json.get(TIMESTAMP).getAsLong();

    // Construct the event object.
    Class<? extends Event> clazz = type.getClazz();
    Constructor<? extends Event> ctor;
    try {
      ctor = clazz.getDeclaredConstructor();
      ctor.setAccessible(true);
      Event event = ctor.newInstance();

      // Set the default fields from AbstractEvent.
      Class<?> rootClass = AbstractEvent.class;
      setField(event, rootClass.getDeclaredField(WAVELET), wavelet);
      setField(event, rootClass.getDeclaredField(MODIFIED_BY), modifiedBy);
      setField(event, rootClass.getDeclaredField(TIMESTAMP), timestamp);
      setField(event, rootClass.getDeclaredField(TYPE), type);
      setField(event, rootClass.getDeclaredField(BUNDLE), bundle);

      JsonObject properties = json.get(PROPERTIES).getAsJsonObject();

      // Set the blip id field, that can be null for certain events, such as
      // OPERATION_ERROR.
      JsonElement blipId = properties.get(BLIP_ID);
      if (blipId != null && !(blipId instanceof JsonNull)) {
        setField(event, rootClass.getDeclaredField(BLIP_ID), blipId.getAsString());
      }

      // Set the additional fields.
      for (Field field : clazz.getDeclaredFields()) {
        String fieldName = field.getName();
        if (properties.has(fieldName)) {
          setField(event, field, context.deserialize(properties.get(fieldName),
              field.getGenericType()));
        }
      }
      return event;
    } catch (NoSuchMethodException e) {
      throw new EventSerializationException("Unable to deserialize event JSON: " + json, json);
    } catch (NoSuchFieldException e) {
      throw new EventSerializationException("Unable to deserialize event JSON: " + json, json);
    } catch (InstantiationException e) {
      throw new EventSerializationException("Unable to deserialize event JSON: " + json, json);
    } catch (IllegalAccessException e) {
      throw new EventSerializationException("Unable to deserialize event JSON: " + json, json);
    } catch (InvocationTargetException e) {
      throw new EventSerializationException("Unable to deserialize event JSON: " + json, json);
    } catch (JsonParseException e) {
      throw new EventSerializationException("Unable to deserialize event JSON: " + json, json);
    }
  }

  /**
   * Extracts event specific properties into a map. This method will not include
   * the basic properties from {@link AbstractEvent}, except for blip id, in the
   * resulting map.
   *
   * @param event the event whose properties will be extracted.
   * @return a map of {@link ParamsProperty} to {@link Object} of properties.
   *
   * @throws EventSerializationException if there is a problem accessing the
   *     event's fields.
   */
  public static Map<ParamsProperty, Object> extractPropertiesToParamsPropertyMap(Event event)
      throws EventSerializationException {
    return extractProperties(event, new KeyConverter<ParamsProperty>() {
      @Override
      public ParamsProperty convert(String key) {
        return ParamsProperty.fromKey(key);
      }
    });
  }

  /**
   * Extracts event specific properties into a map. This method will not include
   * the basic properties from {@link AbstractEvent}, except for blip id, in the
   * resulting map.
   *
   * @param event the event whose properties will be extracted.
   * @return a map of {@link String} to {@link Object} of properties.
   *
   * @throws EventSerializationException if there is a problem accessing the
   *     event's fields.
   */
  public static Map<String, Object> extractPropertiesToStringMap(Event event)
      throws EventSerializationException {
    return extractProperties(event, new KeyConverter<String>() {
      @Override
      public String convert(String key) {
        return key;
      }
    });
  }

  /**
   * Extracts event specific properties into a map. This method will not include
   * the basic properties from {@link AbstractEvent}, except for blip id, in the
   * resulting map.
   *
   * @param event the event whose properties will be extracted.
   * @param keyConverter the converter to convert the event property name into
   *     a proper key object for the resulting map.
   * @return a map of {@code T} to {@link Object} of properties.
   *
   * @throws EventSerializationException if there is a problem accessing the
   *     event's fields.
   */
  private static <T> Map<T, Object> extractProperties(Event event, KeyConverter<T> keyConverter)
      throws EventSerializationException {
    Field[] fields = event.getClass().getDeclaredFields();
    Map<T, Object> data = new HashMap<T, Object>(fields.length + 1);
    try {
      for (Field field : fields) {
        field.setAccessible(true);
        data.put(keyConverter.convert(field.getName()), field.get(event));
      }

      Field field = AbstractEvent.class.getDeclaredField(BLIP_ID);
      field.setAccessible(true);
      data.put(keyConverter.convert(BLIP_ID), field.get(event));
    } catch (IllegalArgumentException e) {
      throw new EventSerializationException(e.getMessage());
    } catch (IllegalAccessException e) {
      throw new EventSerializationException(e.getMessage());
    } catch (NoSuchFieldException e) {
      throw new EventSerializationException(e.getMessage());
    }
    return data;
  }

  /**
   * Sets the field of the given {@link Event} object.
   *
   * @param event the {@link Event} object whose field will be set.
   * @param field the {@link Field} object that represents the field.
   * @param value the value to be set to the field.
   *
   * @throws IllegalAccessException if the field is not accessible.
   */
  private static void setField(Event event, Field field, Object value)
      throws IllegalAccessException {
    field.setAccessible(true);
    field.set(event, value);
  }
}
TOP

Related Classes of com.google.wave.api.event.EventSerializer$KeyConverter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.