Package org.apache.avro.reflect

Source Code of org.apache.avro.reflect.ReflectData$AllowNull

/**
* 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 org.apache.avro.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.apache.avro.AvroRuntimeException;
import org.apache.avro.AvroTypeException;
import org.apache.avro.Protocol;
import org.apache.avro.Schema;
import org.apache.avro.Protocol.Message;
import org.apache.avro.Schema.Type;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericArray;
import org.apache.avro.generic.GenericFixed;
import org.apache.avro.ipc.AvroRemoteException;
import org.apache.avro.util.Utf8;

import com.thoughtworks.paranamer.CachingParanamer;
import com.thoughtworks.paranamer.Paranamer;

/** Utilities to use existing Java classes and interfaces via reflection. */
public class ReflectData extends GenericData {
 
  /** {@link ReflectData} implementation that permits null field values.  The
   * schema generated for each field is a union of its declared type and
   * null. */
  public static class AllowNull extends ReflectData {

    private static final AllowNull INSTANCE = new AllowNull();

    /** Return the singleton instance. */
    public static AllowNull get() { return INSTANCE; }

    protected Schema createFieldSchema(Field field, Map<String, Schema> names) {
      Schema schema = super.createFieldSchema(field, names);
      return Schema.createUnion(Arrays.asList(new Schema[] {
            schema,
            Schema.create(Schema.Type.NULL) }));
    }
  }
 
  private static final ReflectData INSTANCE = new ReflectData();

  protected ReflectData() {}
 
  /** Return the singleton instance. */
  public static ReflectData get() { return INSTANCE; }

  @Override
  protected boolean isRecord(Object datum) {
    return getSchema(datum.getClass()).getType() == Type.RECORD;
  }

  @Override
  protected Schema getRecordSchema(Object record) {
    return getSchema(record.getClass());
  }

  @Override
  protected boolean isEnum(Object datum) {
    return datum instanceof Enum;
  }

  /** Returns true if an object matches a schema. */
  @Override
  public boolean validate(Schema schema, Object datum) {
    switch (schema.getType()) {
    case RECORD:
      Class c = datum.getClass();
      if (!(datum instanceof Object)) return false;
      for (Map.Entry<String, Schema> entry : schema.getFieldSchemas()) {
        try {
          if (!validate(entry.getValue(),
                        ReflectData.getField(c, entry.getKey()).get(datum)))
          return false;
        } catch (IllegalAccessException e) {
          throw new AvroRuntimeException(e);
        }
      }
      return true;
    case ENUM:
      return datum instanceof Enum
        && schema.getEnumSymbols().contains(((Enum)datum).name());
    case ARRAY:
      if (!(datum instanceof GenericArray)) return false;
      for (Object element : (GenericArray)datum)
        if (!validate(schema.getElementType(), element))
          return false;
      return true;
    case UNION:
      for (Schema type : schema.getTypes())
        if (validate(type, datum))
          return true;
      return false;
    case FIXED:   return datum instanceof GenericFixed;
    case STRING:  return datum instanceof Utf8;
    case BYTES:   return datum instanceof ByteBuffer;
    case INT:     return datum instanceof Integer;
    case LONG:    return datum instanceof Long;
    case FLOAT:   return datum instanceof Float;
    case DOUBLE:  return datum instanceof Double;
    case BOOLEAN: return datum instanceof Boolean;
    case NULL:    return datum == null;
    default: return false;
    }
  }

  static Field getField(Class c, String name) {
    try {
      Field f = c.getDeclaredField(name);
      f.setAccessible(true);
      return f;
    } catch (NoSuchFieldException e) {
      throw new AvroRuntimeException(e);
    }
  }

  private final WeakHashMap<java.lang.reflect.Type,Schema> schemaCache =
    new WeakHashMap<java.lang.reflect.Type,Schema>();

  /** Generate a schema for a Java type.
   * <p>For records, {@link Class#getDeclaredFields() declared fields} (not
   * inherited) which are not static or transient are used.</p>
   * <p>Note that unions cannot be automatically generated by this method,
   * since Java provides no representation for unions.</p>
   */
  public Schema getSchema(java.lang.reflect.Type type) {
    Schema schema = schemaCache.get(type);
    if (schema == null) {
      schema = createSchema(type, new LinkedHashMap<String,Schema>());
      schemaCache.put(type, schema);
    }
    return schema;
  }

  /**
   * Create a schema for a type and it's fields. Note that by design only fields
   * of the direct class, not it's super classes, are used for creating the
   * schema.  Also, fields are not permitted to be null.
   */
  @SuppressWarnings(value="unchecked")
  protected Schema createSchema(java.lang.reflect.Type type,
                                Map<String,Schema> names) {
    if (type == Utf8.class)
      return Schema.create(Type.STRING);
    else if (type == ByteBuffer.class)
      return Schema.create(Type.BYTES);
    else if ((type == Integer.class) || (type == Integer.TYPE))
      return Schema.create(Type.INT);
    else if ((type == Long.class) || (type == Long.TYPE))
      return Schema.create(Type.LONG);
    else if ((type == Float.class) || (type == Float.TYPE))
      return Schema.create(Type.FLOAT);
    else if ((type == Double.class) || (type == Double.TYPE))
      return Schema.create(Type.DOUBLE);
    else if ((type == Boolean.class) || (type == Boolean.TYPE))
      return Schema.create(Type.BOOLEAN);
    else if ((type == Void.class) || (type == Void.TYPE))
      return Schema.create(Type.NULL);
    else if (type instanceof ParameterizedType) {
      ParameterizedType ptype = (ParameterizedType)type;
      Class raw = (Class)ptype.getRawType();
      System.out.println("ptype = "+ptype+" raw = "+raw);
      java.lang.reflect.Type[] params = ptype.getActualTypeArguments();
      for (int i = 0; i < params.length; i++)
        System.out.println("param ="+params[i]);
      if (GenericArray.class.isAssignableFrom(raw)) { // array
        if (params.length != 1)
          throw new AvroTypeException("No array type specified.");
        return Schema.createArray(createSchema(params[0], names));
      } else if (Map.class.isAssignableFrom(raw)) { // map
        java.lang.reflect.Type key = params[0];
        java.lang.reflect.Type value = params[1];
        if (!(key == Utf8.class))
          throw new AvroTypeException("Map key class not Utf8: "+key);
        return Schema.createMap(createSchema(value, names));
      }
    } else if (type instanceof Class) {
      Class c = (Class)type;
      String name = c.getSimpleName();
      String space = c.getPackage().getName();
     
      Schema schema = names.get(name);
      if (schema == null) {

        if (c.isEnum()) {                         // enum
          List<String> symbols = new ArrayList<String>();
          Enum[] constants = (Enum[])c.getEnumConstants();
          for (int i = 0; i < constants.length; i++)
            symbols.add(constants[i].name());
          schema = Schema.createEnum(name, space, symbols);
          names.put(name, schema);
          return schema;
        }
                                                  // fixed
        if (GenericFixed.class.isAssignableFrom(c)) {
          int size = ((FixedSize)c.getAnnotation(FixedSize.class)).value();
          schema = Schema.createFixed(name, space, size);
          names.put(name, schema);
          return schema;
        }
                                                  // record
        LinkedHashMap<String,Schema.Field> fields =
          new LinkedHashMap<String,Schema.Field>();
        schema = Schema.createRecord(name, space,
                                     Throwable.class.isAssignableFrom(c));
        if (!names.containsKey(name))
          names.put(name, schema);
        for (Field field : c.getDeclaredFields())
          if ((field.getModifiers()&(Modifier.TRANSIENT|Modifier.STATIC))==0) {
            Schema fieldSchema = createFieldSchema(field, names);
            fields.put(field.getName(), new Schema.Field(fieldSchema, null));
          }
        schema.setFields(fields);
      }
      return schema;
    }
    throw new AvroTypeException("Unknown type: "+type);
  }

  /** Create a schema for a field. */
  protected Schema createFieldSchema(Field field, Map<String, Schema> names) {
    return createSchema(field.getGenericType(), names);
  }

  /** Generate a protocol for a Java interface.
   * <p>Note that this requires that <a
   * href="http://paranamer.codehaus.org/">Paranamer</a> is run over compiled
   * interface declarations, since Java 6 reflection does not provide access to
   * method parameter names.  See Avro's build.xml for an example. </p>
   */
  public Protocol getProtocol(Class iface) {
    Protocol protocol =
      new Protocol(iface.getSimpleName(), iface.getPackage().getName());
    for (Method method : iface.getDeclaredMethods())
      if ((method.getModifiers() & Modifier.STATIC) == 0)
        protocol.getMessages().put(method.getName(),
                                   getMessage(method, protocol));

    // reverse types, since they were defined in reference order
    List<Map.Entry<String,Schema>> names =
      new ArrayList<Map.Entry<String,Schema>>();
    names.addAll(protocol.getTypes().entrySet());
    Collections.reverse(names);
    protocol.getTypes().clear();
    for (Map.Entry<String,Schema> name : names)
      protocol.getTypes().put(name.getKey(), name.getValue());

    return protocol;
  }

  private final Paranamer paranamer = new CachingParanamer();

  private Message getMessage(Method method, Protocol protocol) {
    Map<String,Schema> names = protocol.getTypes();
    LinkedHashMap<String,Schema.Field> fields =
      new LinkedHashMap<String,Schema.Field>();
    String[] paramNames = paranamer.lookupParameterNames(method);
    java.lang.reflect.Type[] paramTypes = method.getGenericParameterTypes();
    for (int i = 0; i < paramTypes.length; i++)
      fields.put(paramNames[i],
                 new Schema.Field(createSchema(paramTypes[i], names), null));
    Schema request = Schema.createRecord(fields);

    Schema response = createSchema(method.getGenericReturnType(), names);

    List<Schema> errs = new ArrayList<Schema>();
    errs.add(Protocol.SYSTEM_ERROR);              // every method can throw
    for (java.lang.reflect.Type err : method.getGenericExceptionTypes())
      if (err != AvroRemoteException.class)
        errs.add(createSchema(err, names));
    Schema errors = Schema.createUnion(errs);

    return protocol.createMessage(method.getName(), request, response, errors);
  }

}
TOP

Related Classes of org.apache.avro.reflect.ReflectData$AllowNull

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.