/*
* Copyright 2010 The Ganshane Team.
*
* 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 corner.protobuf;
import java.util.Collection;
import org.apache.commons.codec.binary.Base64;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.internal.InternalConstants;
import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.services.ClassNameLocator;
import org.apache.tapestry5.ioc.services.Coercion;
import org.apache.tapestry5.services.ValueEncoderFactory;
import corner.orm.model.PaginationOptions;
/**
* 配置<a href="http://code.google.com/p/protobuf/">Google protocol buffer</a>的Value Encoder.
* 提供了通过Object<->String两者之间的转换,方便在查询的时候记录查询条件.
*
* @author dong
* @author <a href="jun.tsai@ganshane.net">Jun Tsai</a>
* @version $Revision$
* @since 0.0.1
*/
public class ProtocolBuffersModule {
public static void contributeValueEncoderSource(MappedConfiguration<Class, ValueEncoderFactory> configuration, final ClassNameLocator classNameLocator, @Inject
@Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
String appRootPackage) {
final String protoPackage = appRootPackage+".protobuf";
final Collection<String> protoClasses = classNameLocator.locateClassNames(protoPackage);
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
for (String className : protoClasses) {
addProtobufferCoercer(configuration,contextClassLoader,className);
}
//add PaginationOptions class type coercer
addProtobufferCoercer(configuration,contextClassLoader, PaginationOptions.class.getName());
}
private static void addProtobufferCoercer(MappedConfiguration<Class, ValueEncoderFactory> configuration,
ClassLoader contextClassLoader,String className){
try {
Class protoClass = contextClassLoader.loadClass(className);
//假如class是ProtocolBuffer的子类,则进行处理
if (ProtocolBuffer.class.isAssignableFrom(protoClass)) {
final
ValueEncoderFactory factory = new ValueEncoderFactory() {
@SuppressWarnings("unchecked")
public ValueEncoder create(Class type) {
return new ProtoValueEncoder(type);
}
};
configuration.add(protoClass,factory);
}
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
/**
* 针对Protobuf类型的对象进行Encoder
* @author dong
* @author <a href="jun.tsai@ganshane.net">Jun Tsai</a>
* @version $Revision$
* @since 0.0.1
*/
public static class ProtoValueEncoder<T extends ProtocolBuffer> implements ValueEncoder<T> {
final Coercion<T, String> proto2string = new Object2ProtoStringCoercion<T>();
final Coercion<String, T> string2proto;
public ProtoValueEncoder(Class<T> type) {
string2proto = new ProtoString2ObjectCoercion<T>(type);
}
/**
* @see org.apache.tapestry5.ValueEncoder#toClient(java.lang.Object)
*/
@Override
public String toClient(T value) {
return proto2string.coerce(value);
}
/**
* @see org.apache.tapestry5.ValueEncoder#toValue(java.lang.String)
*/
@Override
public T toValue(String clientValue) {
return this.string2proto.coerce(clientValue);
}
}
/**
* 从String 到Protobuffer
*
* @author dong
* @author <a href="jun.tsai@ganshane.net">Jun Tsai</a>
* @version $Revision$
* @since 0.0.1
*/
static class ProtoString2ObjectCoercion<T extends ProtocolBuffer> implements Coercion<String, T> {
private final Class<T> type;
public ProtoString2ObjectCoercion(Class<T> type) {
this.type = type;
}
/**
* @see org.apache.tapestry5.ioc.services.Coercion#coerce(java.lang.Object)
*/
@Override
public T coerce(String input) {
if (input == null) {
return null;
}
T obj = null;
try {
obj = this.type.newInstance();
if("X".equals(input)){
return obj;
}
//先通过base64进行解码
byte[] _b64 = Base64.decodeBase64(input.getBytes());
//通过给定的二进制码,然后来反序列化对象
obj.mergeData(_b64);
return obj;
} catch (Throwable te) {
throw new RuntimeException(te);
}
}
}
/**
* 从对象到Protobuf的string的转换
* @author dong
* @author <a href="jun.tsai@ganshane.net">Jun Tsai</a>
* @version $Revision$
* @since 0.0.1
*/
static class Object2ProtoStringCoercion<T extends ProtocolBuffer> implements Coercion<T, String> {
/**
* @see org.apache.tapestry5.ioc.services.Coercion#coerce(java.lang.Object)
*/
@Override
public String coerce(T input) {
if (input == null) {
return null;
}
String str= new String(Base64.encodeBase64(input.getData()));
if(InternalUtils.isBlank(str)){
return "X";
}
return str;
}
}
}