/branches/grupo4/impl/conf/berserk/sd.xml |
---|
4048,6 → 4048,20 |
</filterChains> |
</service> |
<service> |
<name>CourseReportSave</name> |
<implementationClass>pt.estgp.estgweb.services.courses.CourseReportServices</implementationClass> |
<description> |
@reportCourseDocumento faz save de um documento alterado |
</description> |
<isTransactional>true</isTransactional> |
<defaultMethod>saveCourseReport</defaultMethod> |
<filterChains> |
<chain name="Logger"/> |
<chain name="Session"/> |
</filterChains> |
</service> |
</serviceDefinitions> |
/branches/grupo4/impl/src/java/pt/estgp/estgweb/services/courses/CourseReportServices.java |
---|
60,8 → 60,8 |
* @param courseCode |
* @param year |
* @return |
* @throws IOException |
* @throws JSONException |
* @throws java.io.IOException |
* @throws org.json.JSONException |
*/ |
public CourseReportDocument createNewCourseReportDocument(String courseCode,String year) throws IOException, JSONException |
{ |
304,8 → 304,8 |
* @param courseCode |
* @param year |
* @return UnitsDtpTable |
* @throws IOException |
* @throws JSONException |
* @throws java.io.IOException |
* @throws org.json.JSONException |
*/ |
public UnitsDtpTable updateDtpStatsTable4Course(CourseReportDocument reportCourseDocument, String courseCode, String year) throws IOException, JSONException |
{ |
358,8 → 358,8 |
* @param courseCode |
* @param year |
* @param reportCourseDocument |
* @throws JSONException |
* @throws IOException |
* @throws org.json.JSONException |
* @throws java.io.IOException |
* @return a list of CourseUnitSection |
*/ |
public List<DocumentSection> updateCleanCourseUnitSections(String courseCode, String year, CourseReportDocument reportCourseDocument) throws JSONException, IOException { |
412,10 → 412,9 |
* @param reportDocumentJson |
* @param session |
* @return |
* @throws IOException |
* @throws java.io.IOException |
*/ |
public String generateGlobalLearningResultsChartImg(String reportDocumentJson, |
UserSession session) throws IOException { |
public String generateGlobalLearningResultsChartImg(String reportDocumentJson,UserSession session) throws IOException { |
CourseReportDocument reportDocument = CourseReportDocument.fromJson(reportDocumentJson); |
LearningResultsSection learningResultsSection = (LearningResultsSection) reportDocument.findDocumentSection(LearningResultsSection.class); |
473,8 → 472,8 |
* @param courseCode |
* @param year |
* @return |
* @throws JSONException |
* @throws IOException |
* @throws org.json.JSONException |
* @throws java.io.IOException |
*/ |
public String loadCourseUnitDtpStats(String courseCode,String year) throws JSONException, IOException { |
635,18 → 634,21 |
} |
} |
//save |
public String saveCourseReport(String reportDocumentJson,UserSession session){ |
CourseReportDocument reportDocument = CourseReportDocument.fromJson(reportDocumentJson); |
return null; |
} |
public static void main(String[] args) throws IOException, JSONException { |
AbstractDao.getCurrentSession().beginTransaction(); |
/branches/grupo4/impl/src/java/pt/estgp/estgweb/services/courses/CoursesService.java |
---|
66,7 → 66,7 |
* @param id |
* @param initUnits |
* @return |
* @throws ServiceException |
* @throws pt.estgp.estgweb.services.expceptions.ServiceException |
*/ |
public CourseView loadCourse(long id, boolean initUnits) |
throws ServiceException |
96,7 → 96,7 |
* @param code |
* @param initUnits |
* @return |
* @throws ServiceException |
* @throws pt.estgp.estgweb.services.expceptions.ServiceException |
*/ |
public CourseView loadCourseByCode(String code, boolean initUnits) throws ServiceException |
292,7 → 292,7 |
* @param userSession |
* @param c |
* @return |
* @throws JAXBException if XML is not weel formed |
* @throws javax.xml.bind.JAXBException if XML is not weel formed |
*/ |
private void generateXmlJaxbStudiesPlanVersionFromRepositoryOldPlanStream(UserSession userSession, Course c, boolean forceFichaCurricularUrlSet, String systemUrlForUnitPrograms) throws JAXBException |
{ |
562,7 → 562,7 |
* @param school |
* @param type |
* @return |
* @throws JSONException |
* @throws org.json.JSONException |
*/ |
public JSONObject getActiveCoursesForJsonApi(String school,String type) throws JSONException { |
String institutionalCode = null; |
611,9 → 611,9 |
* //TODO REVER |
* @param code |
* @return |
* @throws JSONException |
* @throws IOException |
* @throws JAXBException |
* @throws org.json.JSONException |
* @throws java.io.IOException |
* @throws javax.xml.bind.JAXBException |
*/ |
public JSONObject getCourseDetailForJsonApi(String code) throws JSONException, IOException, JAXBException { |
683,7 → 683,7 |
* |
* @param code |
* @return |
* @throws JSONException |
* @throws org.json.JSONException |
*/ |
public String getCourseStudiesPlanXml(String code,String renew) throws JSONException { |
717,9 → 717,9 |
* @param systemUrl |
* @param setActive |
* @return |
* @throws IOException |
* @throws JSONException |
* @throws JAXBException |
* @throws java.io.IOException |
* @throws org.json.JSONException |
* @throws javax.xml.bind.JAXBException |
*/ |
public String sincronizeCoursesStudiesPlans(String systemUrl,boolean setActive,UserSession sess) throws IOException, JSONException, JAXBException { |
814,8 → 814,8 |
* @param systemUrl |
* @param code |
* @param c |
* @throws IOException |
* @throws JSONException |
* @throws java.io.IOException |
* @throws org.json.JSONException |
*/ |
private void updateCourseComissionMembersAndCourseInfo(String systemUrl, String code, Course c) throws IOException, JSONException |
{ |
1267,8 → 1267,8 |
* chama o serviço tier1 UserRoleConfigService.createNewNormalizedRoleService |
* @param session |
* @return |
* @throws IOException |
* @throws AccessDeniedException |
* @throws java.io.IOException |
* @throws pt.estgp.estgweb.filters.exceptions.AccessDeniedException |
*/ |
public CourseDepartmentImpl newDepartmentRolesFromJson(String json,UserSession session) throws IOException, AccessDeniedException { |
CourseDepartmentImpl courseDepartment = CourseDepartmentImpl.loadFromJson(json); |
/branches/grupo4/impl/src/java/pt/estgp/estgweb/services/courses/coursereport/documentmodel/learningresults/components/GlobalLearningResultsChartImg.java |
---|
27,7 → 27,7 |
* |
* @param results |
* @return the tmp path for generated chart |
* @throws IOException |
* @throws java.io.IOException |
*/ |
public FileUploaded generateChart2tmp(UnitsLearningResultsTable results,CourseReportDocument courseReportDocument) throws IOException |
{ |
/branches/grupo4/impl/src/java/pt/estgp/estgweb/services/users/UserRoleConfigService.java |
---|
62,7 → 62,7 |
* @param newRoleStr |
* @param session |
* @return |
* @throws AccessDeniedException |
* @throws pt.estgp.estgweb.filters.exceptions.AccessDeniedException |
*/ |
public ReplaceRoleResult createNewNormalizedRoleService(String oldRole,String nomeRole,String newRoleStr,UserSession session) throws AccessDeniedException { |
ReplaceRoleResult result; |
/branches/grupo4/impl/src/java/pt/estgp/estgweb/web/controllers/SubServlet.java |
---|
File deleted |
/branches/grupo4/impl/src/java/pt/estgp/estgweb/web/controllers/TesteEngSoftServlet.java |
---|
File deleted |
/branches/grupo4/impl/src/java/pt/estgp/estgweb/web/controllers/courses/CoursesServicesController.java |
---|
2,7 → 2,6 |
import org.apache.struts.action.ActionForm; |
import org.json.JSONObject; |
import pt.estgp.estgweb.services.courses.coursereport.documentmodel.CourseReportDocument; |
import pt.estgp.estgweb.web.controllers.utils.AbstractWidgetAjaxController; |
import pt.estgp.estgweb.web.utils.RequestUtils; |
import pt.utl.ist.berserk.logic.serviceManager.IServiceManager; |
43,18 → 42,5 |
return new JSONObject(json); |
} |
public JSONObject save(ActionForm form,HttpServletRequest request, HttpServletResponse response) throws Throwable { |
String courseReportDocument = request.getParameter("report"); |
CourseReportDocument reportDocument = CourseReportDocument.fromJson(courseReportDocument); |
reportDocument.setCourseName("ASD"); |
return reportDocument.toJsonObject(); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/DefaultConverters.java |
---|
New file |
0,0 → 1,1334 |
package com.owlike.genson.convert; |
import java.io.File; |
import java.lang.reflect.Array; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.Field; |
import java.lang.reflect.GenericArrayType; |
import java.lang.reflect.InvocationTargetException; |
import java.lang.reflect.Type; |
import java.math.BigDecimal; |
import java.math.BigInteger; |
import java.net.MalformedURLException; |
import java.net.URI; |
import java.net.URL; |
import java.sql.Timestamp; |
import java.text.DateFormat; |
import java.text.ParseException; |
import java.text.SimpleDateFormat; |
import java.util.*; |
import com.owlike.genson.*; |
import com.owlike.genson.annotation.*; |
import com.owlike.genson.annotation.HandleBeanView; |
import com.owlike.genson.reflect.BeanProperty; |
import com.owlike.genson.reflect.TypeUtil; |
import com.owlike.genson.stream.JsonType; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
import com.owlike.genson.stream.ValueType; |
import static com.owlike.genson.reflect.TypeUtil.*; |
/** |
* This class contains all default converters and their factories. You can read the source code <a |
* href= |
* "http://code.google.com/p/genson/source/browse/src/main/java/com/owlike/genson/convert/DefaultConverters.java" |
* >here</a> as example on how to implement custom converters and factories. |
* |
* @author eugen |
*/ |
public final class DefaultConverters { |
private DefaultConverters() { |
} |
@HandleClassMetadata |
public static class SetConverter<E> extends CollectionConverter<E> { |
public SetConverter(Class<E> eClass, Converter<E> elementConverter) { |
super(eClass, elementConverter); |
} |
@Override |
protected Collection<E> create() { |
return new HashSet<E>(); |
} |
} |
@HandleClassMetadata |
public static class LinkedListConverter<E> extends CollectionConverter<E> { |
public LinkedListConverter(Class<E> eClass, Converter<E> elementConverter) { |
super(eClass, elementConverter); |
} |
@Override |
protected Collection<E> create() { |
return new LinkedList<E>(); |
} |
} |
@HandleClassMetadata |
public static class TreeSetConverter<E> extends CollectionConverter<E> { |
public TreeSetConverter(Class<E> eClass, Converter<E> elementConverter) { |
super(eClass, elementConverter); |
} |
@Override |
public void serialize(Collection<E> array, ObjectWriter writer, Context ctx) throws Exception { |
TreeSet<E> treeSet = (TreeSet<E>) array; |
if (treeSet.comparator() != null) { |
throw new UnsupportedOperationException("Serialization and deserialization of TreeSet with Comparator is not supported. " + |
"You need to implement a custom Converter to handle it."); |
} |
super.serialize(array, writer, ctx); |
} |
@Override |
protected Collection<E> create() { |
return new TreeSet<E>(); |
} |
} |
@HandleClassMetadata |
public static class LinkedHashSetConverter<E> extends CollectionConverter<E> { |
public LinkedHashSetConverter(Class<E> eClass, Converter<E> elementConverter) { |
super(eClass, elementConverter); |
} |
@Override |
protected Collection<E> create() { |
return new LinkedHashSet<E>(); |
} |
} |
@HandleClassMetadata |
public static class ArrayDequeConverter<E> extends CollectionConverter<E> { |
public ArrayDequeConverter(Class<E> eClass, Converter<E> elementConverter) { |
super(eClass, elementConverter); |
} |
@Override |
protected Collection<E> create() { |
return new ArrayDeque<E>(); |
} |
} |
@HandleClassMetadata |
public static class PriorityQueueConverter<E> extends CollectionConverter<E> { |
public PriorityQueueConverter(Class<E> eClass, Converter<E> elementConverter) { |
super(eClass, elementConverter); |
} |
@Override |
public void serialize(Collection<E> array, ObjectWriter writer, Context ctx) throws Exception { |
PriorityQueue<E> queue = (PriorityQueue<E>) array; |
if (queue.comparator() != null) { |
throw new UnsupportedOperationException("Serialization and deserialization of PriorityQueue with Comparator is not supported. " + |
"You need to implement a custom Converter to handle it."); |
} |
super.serialize(array, writer, ctx); |
} |
@Override |
protected Collection<E> create() { |
return new PriorityQueue<E>(); |
} |
} |
@HandleClassMetadata |
public static class EnumSetConverter<E> extends CollectionConverter<E> { |
private final Class<E> eClass; |
public EnumSetConverter(Class<E> eClass, Converter<E> elementConverter) { |
super(eClass, elementConverter); |
this.eClass = eClass; |
} |
@SuppressWarnings({"unchecked", "rawtypes"}) |
@Override |
protected Collection<E> create() { |
return EnumSet.noneOf((Class) eClass); |
} |
} |
@HandleClassMetadata |
public static class CollectionConverter<E> implements Converter<Collection<E>> { |
@SuppressWarnings("unused") |
private final Class<E> eClass; |
private final Converter<E> elementConverter; |
public CollectionConverter(Class<E> eClass, Converter<E> elementConverter) { |
this.eClass = eClass; |
this.elementConverter = elementConverter; |
} |
public Collection<E> deserialize(ObjectReader reader, Context ctx) throws Exception { |
reader.beginArray(); |
Collection<E> col = create(); |
for (; reader.hasNext(); ) { |
reader.next(); |
E e = elementConverter.deserialize(reader, ctx); |
col.add(e); |
} |
reader.endArray(); |
return col; |
} |
public void serialize(Collection<E> array, ObjectWriter writer, Context ctx) throws Exception { |
writer.beginArray(); |
for (E e : array) { |
elementConverter.serialize(e, writer, ctx); |
} |
writer.endArray(); |
} |
public Converter<E> getElementConverter() { |
return elementConverter; |
} |
protected Collection<E> create() { |
return new ArrayList<E>(); |
} |
} |
public final static class SingleValueAsListFactory implements Factory<Converter<Collection<?>>> { |
public final static SingleValueAsListFactory instance = new SingleValueAsListFactory(); |
Factory<Converter<Collection<?>>> defaultFactory = CollectionConverterFactory.instance; |
private SingleValueAsListFactory() {} |
@Override |
public Converter<Collection<?>> create(Type type, Genson genson) { |
final CollectionConverter defaultConverter = (CollectionConverter) defaultFactory.create(type, genson); |
return new Converter<Collection<?>>() { |
@Override |
public void serialize(Collection object, ObjectWriter writer, Context ctx) throws Exception { |
defaultConverter.serialize(object, writer, ctx); |
} |
@Override |
public Collection deserialize(ObjectReader reader, Context ctx) throws Exception { |
ValueType vt = reader.getValueType(); |
if (vt != ValueType.ARRAY && vt != ValueType.NULL ) { |
Object object = defaultConverter.getElementConverter().deserialize(reader, ctx); |
Collection result = defaultConverter.create(); |
result.add(object); |
return result; |
} else return defaultConverter.deserialize( reader, ctx ); |
} |
}; |
} |
} |
public final static class CollectionConverterFactory implements Factory<Converter<Collection<?>>> { |
public final static CollectionConverterFactory instance = new CollectionConverterFactory(); |
private CollectionConverterFactory() { |
} |
@SuppressWarnings({"rawtypes", "unchecked"}) |
public Converter<Collection<?>> create(Type forType, Genson genson) { |
Converter<?> elementConverter = genson.provideConverter(TypeUtil.getCollectionType(forType)); |
Class<?> parameterRawClass = TypeUtil.getRawClass(TypeUtil.getCollectionType(forType)); |
Class<?> rawClass = getRawClass(forType); |
if (EnumSet.class.isAssignableFrom(rawClass) && parameterRawClass.isEnum()) |
return new EnumSetConverter(parameterRawClass, elementConverter); |
if (LinkedHashSet.class.isAssignableFrom(rawClass)) |
return new LinkedHashSetConverter(parameterRawClass, elementConverter); |
if (TreeSet.class.isAssignableFrom(rawClass)) |
return new TreeSetConverter(parameterRawClass, elementConverter); |
if (Set.class.isAssignableFrom(rawClass)) |
return new SetConverter(parameterRawClass, elementConverter); |
if (LinkedList.class.isAssignableFrom(rawClass)) |
return new LinkedListConverter(parameterRawClass, elementConverter); |
if (ArrayDeque.class.isAssignableFrom(rawClass)) |
return new ArrayDequeConverter(parameterRawClass, elementConverter); |
if (PriorityQueue.class.isAssignableFrom(rawClass)) |
return new PriorityQueueConverter(parameterRawClass, elementConverter); |
return new CollectionConverter(parameterRawClass, elementConverter); |
} |
} |
@HandleClassMetadata |
public static class ArrayConverter<E> implements Converter<Object> { |
private final Class<E> eClass; |
private final Converter<E> elementConverter; |
public ArrayConverter(Class<E> eClass, Converter<E> elementConverter) { |
this.eClass = eClass; |
this.elementConverter = elementConverter; |
} |
public void serialize(Object array, ObjectWriter writer, Context ctx) throws Exception { |
writer.beginArray(); |
int len = Array.getLength(array); |
for (int i = 0; i < len; i++) { |
@SuppressWarnings("unchecked") |
E e = (E) Array.get(array, i); |
elementConverter.serialize(e, writer, ctx); |
} |
writer.endArray(); |
} |
public Object deserialize(ObjectReader reader, Context ctx) throws Exception { |
reader.beginArray(); |
int size = 10; |
Object array = Array.newInstance(eClass, size); |
int idx = 0; |
for (; reader.hasNext(); ) { |
reader.next(); |
if (idx >= size) { |
size = size * 2 + 1; |
array = expandArray(array, idx, size); |
} |
Array.set(array, idx++, elementConverter.deserialize(reader, ctx)); |
} |
reader.endArray(); |
if (idx < size) { |
array = expandArray(array, idx, idx); |
} |
return array; |
} |
private Object expandArray(Object array, int len, int size) { |
Object tmpArray = Array.newInstance(eClass, size); |
System.arraycopy(array, 0, tmpArray, 0, len); |
return tmpArray; |
} |
} |
@HandleClassMetadata |
public final static class ByteArrayConverter implements Converter<byte[]> { |
public static final ByteArrayConverter instance = new ByteArrayConverter(); |
private ByteArrayConverter() { |
} |
@Override |
public void serialize(byte[] object, ObjectWriter writer, Context ctx) { |
writer.writeValue(object); |
} |
@Override |
public byte[] deserialize(ObjectReader reader, Context ctx) { |
return reader.valueAsByteArray(); |
} |
} |
@HandleClassMetadata |
public static class ByteArrayAsIntArrayConverter implements Converter<byte[]> { |
public static final ByteArrayAsIntArrayConverter instance = new ByteArrayAsIntArrayConverter(); |
private ByteArrayAsIntArrayConverter() { |
} |
@Override |
public void serialize(byte[] object, ObjectWriter writer, Context ctx) throws Exception { |
writer.beginArray(); |
for (int i = 0; i < object.length; i++) writer.writeValue(object[i]); |
writer.endArray(); |
} |
@Override |
public byte[] deserialize(ObjectReader reader, Context ctx) throws Exception { |
byte[] array = new byte[256]; |
reader.beginArray(); |
int i; |
for (i = 0; reader.hasNext(); i++) { |
reader.next(); |
Operations.expandArray(array, i, 2); |
array[i] = (byte) reader.valueAsInt(); |
} |
reader.endArray(); |
return Operations.truncateArray(array, i); |
} |
} |
public final static class ArrayConverterFactory implements Factory<Converter<Object>> { |
public final static ArrayConverterFactory instance = new ArrayConverterFactory(); |
private ArrayConverterFactory() { |
} |
@SuppressWarnings({"rawtypes", "unchecked"}) |
public Converter<Object> create(Type forType, Genson genson) { |
if (forType instanceof GenericArrayType |
|| (forType instanceof Class<?> && ((Class<?>) forType).isArray())) { |
if (byte.class.equals(getCollectionType(forType))) { |
return (Converter) ByteArrayConverter.instance; |
} else { |
Converter<?> elementConverter = genson.provideConverter(TypeUtil |
.getCollectionType(forType)); |
return new ArrayConverter(TypeUtil.getRawClass(TypeUtil |
.getCollectionType(forType)), elementConverter); |
} |
} |
return null; |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class StringConverter implements Converter<String> { |
public final static StringConverter instance = new StringConverter(); |
private StringConverter() { |
} |
public void serialize(String value, ObjectWriter writer, Context ctx) { |
writer.writeValue(value); |
} |
public String deserialize(ObjectReader reader, Context ctx) { |
return reader.valueAsString(); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class BooleanConverter implements Converter<Boolean> { |
public final static BooleanConverter instance = new BooleanConverter(); |
private BooleanConverter() { |
} |
public void serialize(Boolean obj, ObjectWriter writer, Context ctx) { |
writer.writeValue(obj.booleanValue()); |
} |
public Boolean deserialize(ObjectReader reader, Context ctx) { |
return reader.valueAsBoolean(); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class IntegerConverter implements Converter<Integer> { |
public final static IntegerConverter instance = new IntegerConverter(); |
private IntegerConverter() { |
} |
public void serialize(Integer obj, ObjectWriter writer, Context ctx) { |
writer.writeValue(obj.intValue()); |
} |
public Integer deserialize(ObjectReader reader, Context ctx) { |
return reader.valueAsInt(); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class LongConverter implements Converter<Long> { |
public final static LongConverter instance = new LongConverter(); |
private LongConverter() { |
} |
public Long deserialize(ObjectReader reader, Context ctx) { |
return reader.valueAsLong(); |
} |
public void serialize(Long obj, ObjectWriter writer, Context ctx) { |
writer.writeValue(obj.longValue()); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class ShortConverter implements Converter<Short> { |
public final static ShortConverter instance = new ShortConverter(); |
private ShortConverter() { |
} |
public Short deserialize(ObjectReader reader, Context ctx) { |
return reader.valueAsShort(); |
} |
public void serialize(Short obj, ObjectWriter writer, Context ctx) { |
writer.writeValue(obj.shortValue()); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class DoubleConverter implements Converter<Double> { |
public final static DoubleConverter instance = new DoubleConverter(); |
private DoubleConverter() { |
} |
public Double deserialize(ObjectReader reader, Context ctx) { |
return reader.valueAsDouble(); |
} |
public void serialize(Double obj, ObjectWriter writer, Context ctx) { |
if (obj.isNaN() || obj.isInfinite()) { |
writer.writeUnsafeValue(obj.toString()); |
} else { |
writer.writeValue(obj.doubleValue()); |
} |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class FloatConverter implements Converter<Float> { |
public final static FloatConverter instance = new FloatConverter(); |
private FloatConverter() { |
} |
public Float deserialize(ObjectReader reader, Context ctx) { |
return reader.valueAsFloat(); |
} |
public void serialize(Float obj, ObjectWriter writer, Context ctx) { |
if (obj.isNaN() || obj.isInfinite()) { |
writer.writeUnsafeValue(obj.toString()); |
} else { |
writer.writeValue(obj.floatValue()); |
} |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class NumberConverter implements Converter<Number> { |
public final static NumberConverter instance = new NumberConverter(); |
private NumberConverter() { |
} |
public Number deserialize(ObjectReader reader, Context ctx) { |
ValueType vt = reader.getValueType(); |
if (ValueType.INTEGER.equals(vt)) |
return reader.valueAsInt(); |
else if (ValueType.DOUBLE.equals(vt)) |
return reader.valueAsDouble(); |
else { |
String value = reader.valueAsString(); |
return "".equals(value) ? null : parse(value, vt); |
} |
} |
public void serialize(Number obj, ObjectWriter writer, Context ctx) { |
if (isSpecialNumber(obj)) writer.writeUnsafeValue(obj.toString()); else writer.writeValue(obj); |
} |
private boolean isSpecialNumber(Number v) { |
if (v instanceof Double || v instanceof Float) { |
Double num = (Double) v; |
return num.isInfinite() || num.isNaN(); |
} else { |
return false; |
} |
} |
private Number parse(String value, ValueType valueType) { |
try { |
if (value.indexOf('.') >= 0) { |
return Double.parseDouble(value); |
} |
long longValue = Long.parseLong(value); |
if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { |
return Integer.valueOf((int) longValue); |
} |
return Long.parseLong(value); |
} catch (NumberFormatException nfe) { |
throw new JsonBindingException("Could not convert input value " + value |
+ " of type " + valueType.toClass() + " to a Number type.", nfe); |
} |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class CharConverter implements Converter<Character> { |
public final static CharConverter instance = new CharConverter(); |
private CharConverter() { |
} |
public void serialize(Character obj, ObjectWriter writer, Context ctx) { |
writer.writeValue(obj.toString()); |
} |
public Character deserialize(ObjectReader reader, Context ctx) { |
String str = reader.valueAsString(); |
if (str.length() > 1) throw new JsonBindingException( |
"Could not convert a string with length greater than 1 to a single char." |
); |
return str.charAt(0); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class ByteConverter implements Converter<Byte> { |
public final static ByteConverter instance = new ByteConverter(); |
private ByteConverter() { |
} |
public void serialize(Byte obj, ObjectWriter writer, Context ctx) { |
writer.writeValue(obj.byteValue()); |
} |
public Byte deserialize(ObjectReader reader, Context ctx) { |
return (byte) reader.valueAsInt(); |
} |
} |
public final static class PrimitiveConverterFactory implements Factory<Converter<?>> { |
public final static PrimitiveConverterFactory instance = new PrimitiveConverterFactory(); |
private PrimitiveConverterFactory() { |
} |
public Converter<?> create(Type type, Genson genson) { |
Class<?> rawClass = TypeUtil.getRawClass(type); |
if (rawClass == Boolean.class || rawClass == boolean.class) return BooleanConverter.instance; |
if (rawClass == Integer.class || rawClass == int.class) return IntegerConverter.instance; |
if (rawClass == Double.class || rawClass == double.class) return DoubleConverter.instance; |
if (rawClass == Long.class || rawClass == long.class) return LongConverter.instance; |
if (rawClass == Short.class || rawClass == short.class) return ShortConverter.instance; |
if (rawClass == Float.class || rawClass == float.class) return FloatConverter.instance; |
if (rawClass == Character.class || rawClass == char.class) return CharConverter.instance; |
if (rawClass == Byte.class || rawClass == byte.class) return ByteConverter.instance; |
return null; |
} |
} |
@HandleClassMetadata |
public static abstract class MapConverter<K, V> implements Converter<Map<K, V>> { |
private final Converter<V> valueConverter; |
private final KeyAdapter<K> keyAdapter; |
public MapConverter(KeyAdapter<K> keyAdapter, Converter<V> valueConverter) { |
this.keyAdapter = keyAdapter; |
this.valueConverter = valueConverter; |
} |
public Map<K, V> deserialize(ObjectReader reader, Context ctx) throws Exception { |
reader.beginObject(); |
Map<K, V> map = create(); |
for (; reader.hasNext(); ) { |
reader.next(); |
map.put(keyAdapter.adapt(reader.name()), valueConverter.deserialize(reader, ctx)); |
} |
reader.endObject(); |
return map; |
} |
public void serialize(Map<K, V> obj, ObjectWriter writer, Context ctx) throws Exception { |
writer.beginObject(); |
for (Map.Entry<K, V> entry : obj.entrySet()) { |
writer.writeName(keyAdapter.adapt(entry.getKey())); |
valueConverter.serialize(entry.getValue(), writer, ctx); |
} |
writer.endObject(); |
} |
protected abstract Map<K, V> create(); |
} |
public final static class HashMapConverter<K, V> extends MapConverter<K, V> { |
public HashMapConverter(KeyAdapter<K> keyAdapter, Converter<V> valueConverter) { |
super(keyAdapter, valueConverter); |
} |
@Override |
protected Map<K, V> create() { |
return new HashMap<K, V>(); |
} |
} |
public final static class HashTableConverter<K, V> extends MapConverter<K, V> { |
public HashTableConverter(KeyAdapter<K> keyAdapter, Converter<V> valueConverter) { |
super(keyAdapter, valueConverter); |
} |
@Override |
protected Map<K, V> create() { |
return new Hashtable<K, V>(); |
} |
} |
@SuppressWarnings("rawtypes") |
public final static class PropertiesConverter extends MapConverter { |
@SuppressWarnings("unchecked") |
public PropertiesConverter(KeyAdapter keyAdapter, Converter valueConverter) { |
super(keyAdapter, valueConverter); |
} |
@Override |
protected Map create() { |
return new Properties(); |
} |
} |
public final static class TreeMapConverter<K, V> extends MapConverter<K, V> { |
public TreeMapConverter(KeyAdapter<K> keyAdapter, Converter<V> valueConverter) { |
super(keyAdapter, valueConverter); |
} |
@Override |
public void serialize(Map<K, V> obj, ObjectWriter writer, Context ctx) throws Exception { |
TreeMap<K, V> treeMap = (TreeMap<K, V>) obj; |
if (((TreeMap<K, V>) obj).comparator() != null) |
throw new UnsupportedOperationException("Serialization and deserialization of TreeMap with Comparator is not supported. " + |
"You need to implement a custom Converter to handle it."); |
super.serialize(obj, writer, ctx); |
} |
@Override |
protected Map<K, V> create() { |
return new TreeMap<K, V>(); |
} |
} |
public final static class LinkedHashMapConverter<K, V> extends MapConverter<K, V> { |
public LinkedHashMapConverter(KeyAdapter<K> keyAdapter, Converter<V> valueConverter) { |
super(keyAdapter, valueConverter); |
} |
@Override |
protected Map<K, V> create() { |
return new LinkedHashMap<K, V>(); |
} |
} |
public static abstract class KeyAdapter<K> { |
public abstract K adapt(String str); |
public abstract String adapt(K key); |
public final static KeyAdapter<Object> runtimeAdapter = new KeyAdapter<Object>() { |
@Override |
public Object adapt(String str) { |
return str; |
} |
@Override |
public String adapt(Object key) { |
return key.toString(); |
} |
}; |
public final static KeyAdapter<String> strAdapter = new KeyAdapter<String>() { |
@Override |
public String adapt(String key) { |
return key; |
} |
}; |
public final static KeyAdapter<Short> shortAdapter = new KeyAdapter<Short>() { |
@Override |
public Short adapt(String str) { |
return Short.parseShort(str); |
} |
@Override |
public String adapt(Short key) { |
return key.toString(); |
} |
}; |
public final static KeyAdapter<Integer> intAdapter = new KeyAdapter<Integer>() { |
@Override |
public Integer adapt(String str) { |
return Integer.parseInt(str); |
} |
@Override |
public String adapt(Integer key) { |
return key.toString(); |
} |
}; |
public final static KeyAdapter<Long> longAdapter = new KeyAdapter<Long>() { |
@Override |
public Long adapt(String str) { |
return Long.parseLong(str); |
} |
@Override |
public String adapt(Long key) { |
return key.toString(); |
} |
}; |
public final static KeyAdapter<Float> floatAdapter = new KeyAdapter<Float>() { |
@Override |
public Float adapt(String str) { |
return Float.parseFloat(str); |
} |
@Override |
public String adapt(Float key) { |
return key.toString(); |
} |
}; |
public final static KeyAdapter<Double> doubleAdapter = new KeyAdapter<Double>() { |
@Override |
public Double adapt(String str) { |
return Double.parseDouble(str); |
} |
@Override |
public String adapt(Double key) { |
return key.toString(); |
} |
}; |
} |
@HandleClassMetadata |
public static class ComplexMapConverter<K, V> implements Converter<Map<K, V>> { |
private final Converter<K> keyConverter; |
private final Converter<V> valueConverter; |
private ComplexMapConverter(Converter<K> keyConverter, Converter<V> valueConverter) { |
super(); |
this.keyConverter = keyConverter; |
this.valueConverter = valueConverter; |
} |
@Override |
public void serialize(Map<K, V> object, ObjectWriter writer, Context ctx) throws Exception { |
writer.beginArray(); |
for (Map.Entry<K, V> entry : object.entrySet()) { |
writer.beginObject().writeName("key"); |
keyConverter.serialize(entry.getKey(), writer, ctx); |
writer.writeName("value"); |
valueConverter.serialize(entry.getValue(), writer, ctx); |
writer.endObject(); |
} |
writer.endArray(); |
} |
@Override |
public Map<K, V> deserialize(ObjectReader reader, Context ctx) throws Exception { |
Map<K, V> map = new HashMap<K, V>(); |
reader.beginArray(); |
while (reader.hasNext()) { |
reader.next(); |
reader.beginObject(); |
K key = null; |
V value = null; |
while (reader.hasNext()) { |
reader.next(); |
if ("key".equals(reader.name())) { |
key = keyConverter.deserialize(reader, ctx); |
} else if ("value".equals(reader.name())) { |
value = valueConverter.deserialize(reader, ctx); |
} |
} |
map.put(key, value); |
reader.endObject(); |
} |
reader.endArray(); |
return map; |
} |
} |
public final static class MapConverterFactory implements Factory<Converter<? extends Map<?, ?>>> { |
public final static MapConverterFactory instance = new MapConverterFactory(); |
private MapConverterFactory() { |
} |
@SuppressWarnings({"rawtypes", "unchecked"}) |
public Converter<? extends Map<?, ?>> create(Type type, Genson genson) { |
// ok this is a fix but not the cleanest one... we make sure it is a parameterized type |
// otherwise we search for map impl in its hierarchy |
Type expandedType = type; |
if (getRawClass(type).getTypeParameters().length == 0) { |
expandedType = expandType(lookupGenericType(Map.class, getRawClass(type)), type); |
} |
Type keyType = typeOf(0, expandedType); |
Type valueType = typeOf(1, expandedType); |
Class<?> keyRawClass = getRawClass(keyType); |
KeyAdapter<?> keyAdapter = keyAdapter(keyRawClass); |
if (keyAdapter != null) |
return createConverter(getRawClass(type), keyAdapter, genson.provideConverter(valueType)); |
else |
return new ComplexMapConverter(genson.provideConverter(keyType), genson.provideConverter(valueType)); |
} |
public static KeyAdapter<?> keyAdapter(Class<?> keyRawClass) { |
if (Object.class.equals(keyRawClass)) return KeyAdapter.runtimeAdapter; |
else if (String.class.equals(keyRawClass)) return KeyAdapter.strAdapter; |
else if (int.class.equals(keyRawClass) || Integer.class.equals(keyRawClass)) |
return KeyAdapter.intAdapter; |
else if (double.class.equals(keyRawClass) || Double.class.equals(keyRawClass)) |
return KeyAdapter.doubleAdapter; |
else if (long.class.equals(keyRawClass) || Long.class.equals(keyRawClass)) |
return KeyAdapter.longAdapter; |
else if (float.class.equals(keyRawClass) || Float.class.equals(keyRawClass)) |
return KeyAdapter.floatAdapter; |
else if (short.class.equals(keyRawClass) || Short.class.equals(keyRawClass)) |
return KeyAdapter.shortAdapter; |
else return null; |
} |
@SuppressWarnings("unchecked") |
private <K, V> MapConverter<K, V> createConverter(Class<?> typeOfMap, |
KeyAdapter<K> keyAdapter, Converter<V> valueConverter) { |
if (Properties.class.equals(typeOfMap)) |
return new PropertiesConverter(keyAdapter, valueConverter); |
if (Hashtable.class.equals(typeOfMap)) |
return new HashTableConverter<K, V>(keyAdapter, valueConverter); |
if (TreeMap.class.equals(typeOfMap)) |
return new TreeMapConverter<K, V>(keyAdapter, valueConverter); |
if (LinkedHashMap.class.equals(typeOfMap)) |
return new LinkedHashMapConverter<K, V>(keyAdapter, valueConverter); |
return new HashMapConverter<K, V>(keyAdapter, valueConverter); |
} |
} |
@SuppressWarnings("rawtypes") |
public static class DateContextualFactory implements ContextualFactory { |
@Override |
public Converter create(BeanProperty property, Genson genson) { |
JsonDateFormat ann = property.getAnnotation(JsonDateFormat.class); |
if (ann != null) { |
Locale locale = ann.lang().isEmpty() ? Locale.getDefault() : new Locale( |
ann.lang()); |
DateFormat dateFormat = ann.value() != null && !ann.value().isEmpty() ? |
new SimpleDateFormat(ann.value(), locale) : SimpleDateFormat.getInstance(); |
if (Date.class.isAssignableFrom(property.getRawClass())) |
return new DateConverter(dateFormat, ann.asTimeInMillis()); |
if (Calendar.class.isAssignableFrom(property.getRawClass())) |
return new CalendarConverter( |
new DateConverter(dateFormat, ann.asTimeInMillis())); |
} |
return null; |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public static class DateConverter implements Converter<Date> { |
private DateFormat dateFormat; |
private final boolean asTimeInMillis; |
public DateConverter() { |
this(SimpleDateFormat.getDateTimeInstance(), true); |
} |
public DateConverter(DateFormat dateFormat, boolean asTimeInMillis) { |
if (dateFormat == null) dateFormat = SimpleDateFormat.getDateTimeInstance(); |
this.dateFormat = dateFormat; |
this.asTimeInMillis = asTimeInMillis; |
} |
public void serialize(Date obj, ObjectWriter writer, Context ctx) { |
if (asTimeInMillis) |
writer.writeValue(obj.getTime()); |
else |
writer.writeUnsafeValue(format(obj)); |
} |
protected synchronized String format(Date date) { |
return dateFormat.format(date); |
} |
public Date deserialize(ObjectReader reader, Context ctx) { |
try { |
ValueType valueType = reader.getValueType(); |
if (valueType == ValueType.INTEGER) |
return new Date(reader.valueAsLong()); |
else if (valueType == ValueType.STRING) |
return read(reader.valueAsString()); |
else throw new JsonBindingException(String.format("Can not deserialize type %s to Date, " + |
"only numeric and string accepted.", valueType)); |
} catch (ParseException e) { |
throw new JsonBindingException("Could not parse date " + reader.valueAsString(), |
e); |
} |
} |
protected synchronized Date read(String dateString) throws ParseException { |
return dateFormat.parse(dateString); |
} |
} |
public final static class UntypedConverterFactory implements Factory<Converter<Object>> { |
public final static UntypedConverterFactory instance = new UntypedConverterFactory(); |
private UntypedConverterFactory() { |
} |
public final static class UntypedConverter implements Converter<Object> { |
final static UntypedConverter instance = new UntypedConverter(); |
private UntypedConverter() { |
} |
public Object deserialize(ObjectReader reader, Context ctx) { |
return ctx.genson.deserialize(GenericType.of(reader.getValueType().toClass()), |
reader, ctx); |
} |
public void serialize(Object obj, ObjectWriter writer, Context ctx) { |
if (Object.class.equals(obj.getClass())) |
throw new UnsupportedOperationException( |
"Serialization of type Object is not supported by default serializers."); |
ctx.genson.serialize(obj, obj.getClass(), writer, ctx); |
} |
} |
public Converter<Object> create(Type type, Genson genson) { |
if (TypeUtil.match(type, Object.class, true)) { |
return UntypedConverter.instance; |
} |
return null; |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public static class EnumConverter<T extends Enum<T>> implements Converter<T> { |
private final Class<T> eClass; |
private final Map<String, T> deserializationNames; |
private final boolean caseSensitive; |
public EnumConverter(Class<T> eClass, boolean caseSensitive) { |
this.eClass = eClass; |
this.caseSensitive = caseSensitive; |
deserializationNames = new HashMap<String, T>(); |
for (Field f : eClass.getFields()) { |
try { |
if (!f.isEnumConstant()) { |
continue; |
} |
String name = caseSensitive ? f.getName(): f.getName().toUpperCase(); |
deserializationNames.put(name, (T) f.get(null)); |
} catch (IllegalAccessException e) { |
throw new JsonBindingException("Failed to get enum value " + f.getName(), e); |
} |
} |
} |
public void serialize(T obj, ObjectWriter writer, Context ctx) { |
writer.writeUnsafeValue(obj.name()); |
} |
public T deserialize(ObjectReader reader, Context ctx) { |
String name = caseSensitive ? reader.valueAsString(): reader.valueAsString().toUpperCase(); |
T value = deserializationNames.get(name); |
if (value == null) throw new JsonBindingException("No enum constant " + eClass.getCanonicalName() + "." + name); |
return value; |
} |
} |
public final static class EnumConverterFactory implements Factory<Converter<? extends Enum<?>>> { |
public final static EnumConverterFactory instance = new EnumConverterFactory(true); |
public final boolean caseSensitive; |
public EnumConverterFactory(boolean caseSensitive) { |
this.caseSensitive = caseSensitive; |
} |
@SuppressWarnings({"rawtypes", "unchecked"}) |
public Converter<Enum<?>> create(Type type, Genson genson) { |
Class<?> rawClass = TypeUtil.getRawClass(type); |
return rawClass.isEnum() || Enum.class.isAssignableFrom(rawClass) ? new EnumConverter( |
rawClass, caseSensitive) : null; |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public static class URLConverter implements Converter<URL> { |
public final static URLConverter instance = new URLConverter(); |
private URLConverter() { |
} |
public URL deserialize(ObjectReader reader, Context ctx) { |
try { |
return new URL(reader.valueAsString()); |
} catch (MalformedURLException e) { |
throw new JsonBindingException("Can not deserializer <" + reader.valueAsString() + "> to URL."); |
} |
} |
public void serialize(URL object, ObjectWriter writer, Context ctx) { |
writer.writeValue(object.toExternalForm()); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public static class URIConverter implements Converter<URI> { |
public final static URIConverter instance = new URIConverter(); |
private URIConverter() { |
} |
public void serialize(URI object, ObjectWriter writer, Context ctx) { |
writer.writeUnsafeValue(object.toString()); |
} |
public URI deserialize(ObjectReader reader, Context ctx) { |
return URI.create(reader.valueAsString()); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public static class BigDecimalConverter implements Converter<BigDecimal> { |
public final static BigDecimalConverter instance = new BigDecimalConverter(); |
private BigDecimalConverter() { |
} |
@Override |
public BigDecimal deserialize(ObjectReader reader, Context ctx) { |
return new BigDecimal(reader.valueAsString()); |
} |
@Override |
public void serialize(BigDecimal object, ObjectWriter writer, Context ctx) { |
writer.writeValue(object); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public static class BigIntegerConverter implements Converter<BigInteger> { |
public final static BigIntegerConverter instance = new BigIntegerConverter(); |
private BigIntegerConverter() { |
} |
@Override |
public BigInteger deserialize(ObjectReader reader, Context ctx) { |
return new BigInteger(reader.valueAsString()); |
} |
@Override |
public void serialize(BigInteger object, ObjectWriter writer, Context ctx) { |
writer.writeValue(object); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public static class TimestampConverter implements Converter<Timestamp> { |
public final static TimestampConverter instance = new TimestampConverter(); |
private TimestampConverter() { |
} |
@Override |
public Timestamp deserialize(ObjectReader reader, Context ctx) { |
return Timestamp.valueOf(reader.valueAsString()); |
} |
@Override |
public void serialize(Timestamp object, ObjectWriter writer, Context ctx) { |
writer.writeValue(object.toString()); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public static class UUIDConverter implements Converter<UUID> { |
public final static UUIDConverter instance = new UUIDConverter(); |
private UUIDConverter() { |
} |
@Override |
public void serialize(UUID object, ObjectWriter writer, Context ctx) { |
writer.writeValue(object.toString()); |
} |
@Override |
public UUID deserialize(ObjectReader reader, Context ctx) { |
return UUID.fromString(reader.valueAsString()); |
} |
} |
public final static class CalendarConverterFactory implements Factory<Converter<Calendar>> { |
private final CalendarConverter calendarConverter; |
public CalendarConverterFactory(DateConverter dateConverter) { |
this.calendarConverter = new CalendarConverter(dateConverter); |
} |
@Override |
public Converter<Calendar> create(Type type, Genson genson) { |
if (!Calendar.class.isAssignableFrom(TypeUtil.getRawClass(type))) |
throw new IllegalStateException( |
"CalendarConverterFactory create method can be called only for Calendar type and subtypes."); |
return calendarConverter; |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public static class CalendarConverter implements Converter<Calendar> { |
private final DateConverter dateConverter; |
CalendarConverter(final DateConverter dateConverter) { |
this.dateConverter = dateConverter; |
} |
@Override |
public void serialize(Calendar object, ObjectWriter writer, Context ctx) { |
dateConverter.serialize(object.getTime(), writer, ctx); |
} |
@Override |
public Calendar deserialize(ObjectReader reader, Context ctx) { |
Calendar cal = null; |
if (ValueType.NULL != reader.getValueType()) { |
cal = new GregorianCalendar(); |
cal.setTime(dateConverter.deserialize(reader, ctx)); |
} |
return cal; |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
public final static class FileConverter implements Converter<File> { |
public final static FileConverter instance = new FileConverter(); |
private FileConverter() { |
} |
@Override |
public void serialize(File object, ObjectWriter writer, Context ctx) { |
writer.writeValue(object.getPath()); |
} |
@Override |
public File deserialize(ObjectReader reader, Context ctx) { |
return new File(reader.valueAsString()); |
} |
} |
public final static class PropertyConverterFactory implements ContextualFactory<Object> { |
@SuppressWarnings("unchecked") |
@Override |
public Converter<Object> create(BeanProperty property, Genson genson) { |
JsonConverter ann = property.getAnnotation(JsonConverter.class); |
if (ann != null) { |
Type converterExpandedType = expandType( |
lookupGenericType(Converter.class, ann.value()), ann.value()); |
Type converterPropertyType = typeOf(0, converterExpandedType); |
Class<?> propertyClass = property.getRawClass(); |
if (propertyClass.isPrimitive()) propertyClass = wrap(propertyClass); |
// checking type consistency |
if (!match(propertyClass, converterPropertyType, false)) |
throw new ClassCastException("The type defined in " + ann.value().getName() |
+ " is not assignale from property " + property.getName() |
+ " declared in " + property.getDeclaringClass()); |
try { |
Constructor<?> ctr = ann.value().getConstructor(); |
if (!ctr.isAccessible()) ctr.setAccessible(true); |
return (Converter<Object>) ctr.newInstance(); |
// OMG... |
} catch (InstantiationException e) { |
throw new RuntimeException(e); |
} catch (IllegalAccessException e) { |
throw new RuntimeException(e); |
} catch (SecurityException e) { |
throw new RuntimeException(e); |
} catch (NoSuchMethodException e) { |
throw new RuntimeException(e); |
} catch (IllegalArgumentException e) { |
throw new RuntimeException(e); |
} catch (InvocationTargetException e) { |
throw new RuntimeException(e); |
} |
} |
return null; |
} |
} |
public static class WrappedRootValueConverter<T> implements Converter<T> { |
private final String inputName; |
private final String outputName; |
private final Converter<T> delegateConverter; |
public WrappedRootValueConverter(String inputName, String outputName, Converter<T> delegateConverter) { |
this.inputName = inputName; |
this.outputName = outputName; |
this.delegateConverter = delegateConverter; |
} |
@Override |
public void serialize(T object, ObjectWriter writer, Context ctx) throws Exception { |
if (writer.enclosingType() == JsonType.EMPTY) { |
writer.beginObject().writeName(outputName); |
delegateConverter.serialize(object, writer, ctx); |
writer.endObject(); |
} |
} |
@Override |
public T deserialize(ObjectReader reader, Context ctx) throws Exception { |
T value = null; |
if (reader.enclosingType() == JsonType.EMPTY) { |
reader.beginObject(); |
// Lets accept the case where the key is missing |
if (reader.hasNext()) { |
reader.next(); |
if (!inputName.equalsIgnoreCase(reader.name())) { |
throw new JsonBindingException( |
String.format( |
"Expected key %s for unwrapping the value, but encountered key %s", |
inputName, |
reader.name() |
) |
); |
} |
value = delegateConverter.deserialize(reader, ctx); |
} |
reader.endObject(); |
} |
return value; |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/ChainedFactory.java |
---|
New file |
0,0 → 1,141 |
package com.owlike.genson.convert; |
import java.lang.reflect.Type; |
import com.owlike.genson.Converter; |
import com.owlike.genson.Factory; |
import com.owlike.genson.Genson; |
/** |
* A chained factory of Converters that gives the ability to implementations to decorate the |
* converter created by the next factory. |
* <p/> |
* One of Genson big strengths is its extensive use of Decorator and Chain of Responsibility design |
* patterns. Chain of responsibility is applied in chaining Factories that can build Decorated |
* Converters. ChainedFactory is the base class for such factories. But as ChainedFactory next |
* element is an instance of Factory, you are free to apply the chain principle for your custom |
* factories differently if you want. |
* <p/> |
* The global idea behind this design is to provide great extensibility to the library by allowing |
* to add new functionalities to existing converters in a non intrusive way (extension and source |
* modification). This is achieved by applying the decorator pattern. Here is an example of a |
* decorated converter that adds null handling support. |
* <p/> |
* <pre> |
* public class NullConverter extends Wrapper<Converter<Object>> implements |
* Converter<Object> { |
* private final Converter<Object> converter; |
* |
* public NullConverter(Converter<Object> converter) { |
* super(converter); |
* this.converter = converter; |
* } |
* |
* public void serialize(Object obj, ObjectWriter writer, Context ctx) { |
* if (obj == null) |
* writer.writeNull(); |
* else |
* converter.serialize(obj, writer, ctx); |
* } |
* |
* public Object deserialize(ObjectReader reader, Context ctx) { |
* if (TypeValue.NULL == reader.getTypeValue()) { |
* return null; |
* } else |
* return converter.deserialize(reader, ctx); |
* |
* } |
* } |
* |
* // now we need a factory to create the nullconverter for type T and wire it with the existing |
* // factories so we can get an instance of the converter for that type. |
* public class NullConverterFactory extends ChainedFactory { |
* public NullConverterFactory(Factory<Converter<?>> next) { |
* super(next); |
* } |
* |
* public Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) { |
* return new NullConverter(nextConverter); |
* } |
* } |
* </pre> |
* <p/> |
* As you can see it is pretty simple but also powerful. Note that our NullConverter extends |
* Wrapper class. When you encapsulate converters you should extend Wrapper |
* class this way Genson can access the class information of wrapped converters. Imagine for example |
* that you put some annotation on converter A and wrap it in converter B, now if you wrap B in C |
* you wont be able to get class information of A (ex: its annotations). Wrapper class |
* allows to merge class information of current implementation and the wrapped one. |
* |
* @author eugen |
* @see com.owlike.genson.convert.NullConverter NullConverter |
* @see com.owlike.genson.convert.BasicConvertersFactory BasicConvertersFactory |
* @see com.owlike.genson.Wrapper Wrapper |
*/ |
public abstract class ChainedFactory implements Factory<Converter<?>> { |
private Factory<? extends Converter<?>> next; |
protected ChainedFactory() { |
} |
protected ChainedFactory(Factory<Converter<?>> next) { |
this.next = next; |
} |
public Converter<?> create(Type type, Genson genson) { |
Converter<?> nextConverter = null; |
if (next != null) { |
nextConverter = next.create(type, genson); |
} |
Converter<?> converter = create(type, genson, nextConverter); |
return converter == null ? nextConverter : converter; |
} |
/** |
* This method will be called by {@link #create(Type, Genson)} with nextConverter being the |
* converter created for current type by the next factory. This means that ChainedFactory will |
* first create a converter with the next factory and then use it's own create method. |
* |
* @param type for which this factory must provide a converter |
* @param genson instance that you can use when you need a converter for some other type (for |
* example a converter of List<Integer> will need a converter for Integer type). |
* @param nextConverter created by the next factory, may be null. |
* @return null or a converter for this type |
*/ |
protected abstract Converter<?> create(Type type, Genson genson, Converter<?> nextConverter); |
/** |
* Chains this factory with next and returns next (the tail) so you can do things like |
* chain1.withNext(new chain2).withNext(new chain3); the resulting chain is |
* chain1=>chain2=>chain3. Don't forget to keep a reference to the head (chain1). |
* |
* @param next factory |
* @return the next factory passed as argument |
*/ |
public final <T extends Factory<? extends Converter<?>>> T withNext(T next) { |
if (this.next != null) |
throw new IllegalStateException("next factory has already been set for " + getClass() |
+ " you can not override it!"); |
this.next = next; |
return next; |
} |
public final <T extends Factory<? extends Converter<?>>> T append(T next) { |
ChainedFactory f = this; |
while(f.next() != null) { |
if (!(f.next() instanceof ChainedFactory)) { |
throw new UnsupportedOperationException("Last element in the chain is not a ChainedFactory"); |
} |
f = (ChainedFactory) f.next(); |
} |
return f.withNext(next); |
} |
/** |
* @return a reference to the next factory, may be null. |
*/ |
public final Factory<? extends Converter<?>> next() { |
return next; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/BasicConvertersFactory.java |
---|
New file |
0,0 → 1,167 |
package com.owlike.genson.convert; |
import java.lang.annotation.Annotation; |
import java.lang.reflect.Type; |
import java.util.Iterator; |
import java.util.List; |
import java.util.Map; |
import com.owlike.genson.Context; |
import com.owlike.genson.Converter; |
import com.owlike.genson.Deserializer; |
import com.owlike.genson.Factory; |
import com.owlike.genson.Genson; |
import com.owlike.genson.Operations; |
import com.owlike.genson.Serializer; |
import com.owlike.genson.Wrapper; |
import com.owlike.genson.reflect.BeanDescriptorProvider; |
import com.owlike.genson.reflect.TypeUtil; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
/** |
* This is the base factory that will create converters based on the default ones and on custom |
* Serializer, Deserializer and Converter. But it also uses factories (default and custom) and |
* {@link com.owlike.genson.reflect.BeanDescriptorProvider BeanDescriptorProvider} that is |
* responsible of creating bean converters. |
* <p/> |
* When you ask for a Converter it will |
* <ul> |
* <ol> |
* <li>Lookup in the registered Serializers for one that is parameterized with the current type, if |
* found we finished (it takes the first one, so the order matters).</li> |
* <li>Else we will try the factories by searching the ones that can create and instance of |
* Serializer<CurrentType> (again the order is very important). We continue while they return |
* null.</li> |
* <li>If no factory could create an instance we will use BeanDescriptorProvider.</li> |
* </ol> |
* </li> |
* <li>We apply all the same logic a second time for Deserializer.</li> |
* <li>If they are both an instance of Converter then we return one of them</li> |
* <li>Otherwise we will wrap both into a Converter.</li> |
* </ul> |
* <p/> |
* Note that the create method from the registered factories will only be called if the type with |
* which they are parameterized is assignable from the current type. For example, if we look for a |
* serializer of Integer then Factory<Converter<Integer>> and Factory<Serializer<Object>> match |
* both, the first registered will be used. |
* |
* @author eugen |
*/ |
public class BasicConvertersFactory implements Factory<Converter<?>> { |
private final Map<Type, Serializer<?>> serializersMap; |
private final Map<Type, Deserializer<?>> deserializersMap; |
private final List<Factory<?>> factories; |
private final BeanDescriptorProvider beanDescriptorProvider; |
public BasicConvertersFactory(Map<Type, Serializer<?>> serializersMap, |
Map<Type, Deserializer<?>> deserializersMap, List<Factory<?>> factories, |
BeanDescriptorProvider beanDescriptorProvider) { |
this.serializersMap = serializersMap; |
this.deserializersMap = deserializersMap; |
this.factories = factories; |
this.beanDescriptorProvider = beanDescriptorProvider; |
} |
@SuppressWarnings({"unchecked", "rawtypes"}) |
public Converter<?> create(Type type, Genson genson) { |
Converter<?> converter; |
Serializer<?> serializer = provide(Serializer.class, type, serializersMap, genson); |
Deserializer<?> deserializer = provide(Deserializer.class, type, deserializersMap, genson); |
if (serializer instanceof Converter && deserializer instanceof Converter) { |
converter = (Converter<?>) deserializer; |
} else { |
converter = new DelegatedConverter(serializer, deserializer); |
} |
return converter; |
} |
@SuppressWarnings("unchecked") |
protected <T> T provide(Class<T> forClass, Type withParameterType, |
Map<Type, ? extends T> fromTypeMap, Genson genson) { |
if (fromTypeMap.containsKey(withParameterType)) return fromTypeMap.get(withParameterType); |
Type wrappedParameterType = withParameterType; |
if (withParameterType instanceof Class<?> && ((Class<?>) withParameterType).isPrimitive()) |
wrappedParameterType = TypeUtil.wrap((Class<?>) withParameterType); |
for (Iterator<Factory<?>> it = factories.iterator(); it.hasNext(); ) { |
Factory<?> factory = it.next(); |
Object object; |
Type factoryType = TypeUtil.lookupGenericType(Factory.class, factory.getClass()); |
factoryType = TypeUtil.expandType(factoryType, factory.getClass()); |
// it is a parameterized type and we want the parameter corresponding to Serializer from |
// Factory<Serializer<?>> |
factoryType = TypeUtil.typeOf(0, factoryType); |
Type factoryParameter = TypeUtil.typeOf(0, factoryType); |
if (forClass.isAssignableFrom(TypeUtil.getRawClass(factoryType)) |
&& TypeUtil.match(wrappedParameterType, factoryParameter, false) |
&& (object = factory.create(withParameterType, genson)) != null) { |
return forClass.cast(object); |
} |
} |
return (T) beanDescriptorProvider.provide(TypeUtil.getRawClass(withParameterType), |
withParameterType, genson); |
} |
private class DelegatedConverter<T> extends Wrapper<Converter<T>> implements Converter<T> { |
private final Serializer<T> serializer; |
private final Deserializer<T> deserializer; |
public DelegatedConverter(Serializer<T> serializer, Deserializer<T> deserializer) { |
this.serializer = serializer; |
this.deserializer = deserializer; |
} |
public void serialize(T obj, ObjectWriter writer, Context ctx) throws Exception { |
serializer.serialize(obj, writer, ctx); |
} |
public T deserialize(ObjectReader reader, Context ctx) throws Exception { |
return deserializer.deserialize(reader, ctx); |
} |
@Override |
public <A extends Annotation> A getAnnotation(Class<A> aClass) { |
A a = null; |
if (serializer != null) a = toAnnotatedElement(serializer).getAnnotation(aClass); |
if (deserializer != null && a == null) |
a = toAnnotatedElement(deserializer).getAnnotation(aClass); |
return a; |
} |
@Override |
public Annotation[] getAnnotations() { |
if (serializer != null && deserializer != null) |
return Operations.union(Annotation[].class, toAnnotatedElement(serializer) |
.getAnnotations(), toAnnotatedElement(deserializer).getAnnotations()); |
if (serializer != null) return toAnnotatedElement(serializer).getAnnotations(); |
if (deserializer != null) return toAnnotatedElement(deserializer).getAnnotations(); |
return new Annotation[0]; |
} |
@Override |
public Annotation[] getDeclaredAnnotations() { |
if (serializer != null && deserializer != null) |
return Operations.union(Annotation[].class, toAnnotatedElement(serializer) |
.getDeclaredAnnotations(), toAnnotatedElement(deserializer) |
.getDeclaredAnnotations()); |
if (serializer != null) return toAnnotatedElement(serializer).getDeclaredAnnotations(); |
if (deserializer != null) |
return toAnnotatedElement(deserializer).getDeclaredAnnotations(); |
return new Annotation[0]; |
} |
@Override |
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { |
if (serializer != null) |
return toAnnotatedElement(serializer).isAnnotationPresent(annotationClass); |
if (deserializer != null) |
return toAnnotatedElement(deserializer).isAnnotationPresent(annotationClass); |
return false; |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/package-info.java |
---|
New file |
0,0 → 1,7 |
/** |
* This package provides some default {@link com.owlike.genson.Converter Converter} implementations and |
* the chained factory and converter decorator mechanisms. |
* @see com.owlike.genson.convert.ChainedFactory |
* @see com.owlike.genson.Wrapper |
*/ |
package com.owlike.genson.convert; |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/BeanViewConverter.java |
---|
New file |
0,0 → 1,104 |
package com.owlike.genson.convert; |
import java.lang.reflect.Type; |
import java.util.List; |
import com.owlike.genson.BeanView; |
import com.owlike.genson.Context; |
import com.owlike.genson.Converter; |
import com.owlike.genson.Genson; |
import com.owlike.genson.Wrapper; |
import com.owlike.genson.annotation.HandleBeanView; |
import com.owlike.genson.reflect.BeanDescriptor; |
import com.owlike.genson.reflect.BeanViewDescriptorProvider; |
import com.owlike.genson.reflect.TypeUtil; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
/** |
* Converter responsible of applying the BeanView mechanism. |
* |
* @param <T> type of objects this BeanViewConverter can handle. |
* @author eugen |
* @see com.owlike.genson.reflect.BeanViewDescriptorProvider BeanViewDescriptorProvider |
* @see com.owlike.genson.BeanView BeanView |
*/ |
public class BeanViewConverter<T> extends Wrapper<Converter<T>> implements Converter<T> { |
public static class BeanViewConverterFactory extends ChainedFactory { |
private final BeanViewDescriptorProvider provider; |
public BeanViewConverterFactory(BeanViewDescriptorProvider provider) { |
this.provider = provider; |
} |
@SuppressWarnings({"rawtypes", "unchecked"}) |
@Override |
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) { |
if (!Wrapper.toAnnotatedElement(nextConverter).isAnnotationPresent( |
HandleBeanView.class)) |
// TODO as we link an instance to a type, we may optimize things, but for the moment it is okay |
// lets see if this feature is used |
return new BeanViewConverter(type, provider, nextConverter); |
return nextConverter; |
} |
} |
private final BeanViewDescriptorProvider provider; |
private final Type type; |
public BeanViewConverter(Type type, BeanViewDescriptorProvider provider, Converter<T> next) { |
super(next); |
this.provider = provider; |
this.type = type; |
} |
@SuppressWarnings("unchecked") |
protected Class<? extends BeanView<T>> findViewFor(Type type, |
List<Class<? extends BeanView<?>>> views) { |
for (Class<? extends BeanView<?>> v : views) { |
Type searchedType = TypeUtil.lookupGenericType(BeanView.class, v); |
searchedType = TypeUtil.expandType(searchedType, v); |
searchedType = TypeUtil.typeOf(0, searchedType); |
if (TypeUtil.match(type, searchedType, false)) { |
return (Class<? extends BeanView<T>>) v; |
} |
} |
return null; |
} |
public void serialize(T obj, ObjectWriter writer, Context ctx) throws Exception { |
boolean handled = false; |
List<Class<? extends BeanView<?>>> views = ctx.views(); |
if (views != null && views.size() > 0) { |
Class<? extends BeanView<T>> viewClass = findViewFor(type, views); |
if (viewClass != null) { |
Type viewForType = TypeUtil.expandType(BeanView.class.getTypeParameters()[0], |
viewClass); |
@SuppressWarnings("unchecked") |
Class<T> viewForClass = (Class<T>) TypeUtil.getRawClass(viewForType); |
BeanDescriptor<T> descriptor = provider |
.provide(viewForClass, viewClass, ctx.genson); |
descriptor.serialize(obj, writer, ctx); |
handled = true; |
} |
} |
if (!handled) wrapped.serialize(obj, writer, ctx); |
} |
public T deserialize(ObjectReader reader, Context ctx) throws Exception { |
if (ctx.hasViews()) { |
Class<? extends BeanView<T>> viewClass = findViewFor(type, ctx.views()); |
if (viewClass != null) { |
Type viewForType = TypeUtil.expandType(BeanView.class.getTypeParameters()[0], |
viewClass); |
@SuppressWarnings("unchecked") |
Class<T> viewForClass = (Class<T>) TypeUtil.getRawClass(viewForType); |
BeanDescriptor<T> descriptor = provider |
.provide(viewForClass, viewClass, ctx.genson); |
return descriptor.deserialize(reader, ctx); |
} |
} |
return wrapped.deserialize(reader, ctx); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/CircularClassReferenceConverterFactory.java |
---|
New file |
0,0 → 1,68 |
package com.owlike.genson.convert; |
import java.lang.reflect.Type; |
import java.util.HashMap; |
import java.util.Map; |
import com.owlike.genson.Context; |
import com.owlike.genson.Converter; |
import com.owlike.genson.Genson; |
import com.owlike.genson.Wrapper; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
/** |
* ChainedFactory that handles circular class references. |
* |
* @author eugen |
*/ |
public class CircularClassReferenceConverterFactory extends ChainedFactory { |
private final static class CircularConverter<T> extends Wrapper<Converter<T>> implements Converter<T> { |
protected CircularConverter() { |
super(); |
} |
public void serialize(T obj, ObjectWriter writer, Context ctx) throws Exception { |
wrapped.serialize(obj, writer, ctx); |
} |
public T deserialize(ObjectReader reader, Context ctx) throws Exception { |
return wrapped.deserialize(reader, ctx); |
} |
void setDelegateConverter(Converter<T> delegate) { |
decorate(delegate); |
} |
} |
private final ThreadLocal<Map<Type, CircularConverter<?>>> _circularConverters = new ThreadLocal<Map<Type, CircularConverter<?>>>(); |
@Override |
@SuppressWarnings({"rawtypes", "unchecked"}) |
public Converter<?> create(Type type, Genson genson) { |
Map<Type, CircularConverter<?>> map = _circularConverters.get(); |
if (map == null) { |
map = new HashMap<Type, CircularConverter<?>>(); |
_circularConverters.set(map); |
} |
if (_circularConverters.get().containsKey(type)) { |
return _circularConverters.get().get(type); |
} else { |
try { |
CircularConverter circularConverter = new CircularConverter(); |
_circularConverters.get().put(type, circularConverter); |
Converter converter = next().create(type, genson); |
circularConverter.setDelegateConverter(converter); |
return converter; |
} finally { |
_circularConverters.get().remove(type); |
} |
} |
} |
@Override |
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) { |
throw new UnsupportedOperationException(); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/NullConverterFactory.java |
---|
New file |
0,0 → 1,96 |
package com.owlike.genson.convert; |
import java.lang.reflect.Type; |
import com.owlike.genson.Context; |
import com.owlike.genson.Converter; |
import com.owlike.genson.Genson; |
import com.owlike.genson.JsonBindingException; |
import com.owlike.genson.Serializer; |
import com.owlike.genson.Wrapper; |
import com.owlike.genson.annotation.HandleNull; |
import com.owlike.genson.reflect.TypeUtil; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
import com.owlike.genson.stream.ValueType; |
/** |
* The default implementation handles null values by returning the predefined default value if any or null during |
* deserialization and by calling writer.writeNull() during serialization. |
* <p/> |
* |
* @author eugen |
*/ |
public class NullConverterFactory extends ChainedFactory { |
private final boolean failOnNullPrimitive; |
public NullConverterFactory(boolean failOnNullPrimitive) { |
this.failOnNullPrimitive = failOnNullPrimitive; |
} |
private class FailIfNullConverter<T> extends Wrapper<Converter<T>> implements Converter<T> { |
public FailIfNullConverter(Converter<T> delegate) { |
super(delegate); |
} |
@Override |
public void serialize(T object, ObjectWriter writer, Context ctx) throws Exception { |
if (object == null) { |
throw new JsonBindingException("Serialization of null primitives is forbidden"); |
} else { |
wrapped.serialize(object, writer, ctx); |
} |
} |
@Override |
public T deserialize(ObjectReader reader, Context ctx) throws Exception { |
if (ValueType.NULL == reader.getValueType()) { |
throw new JsonBindingException("Can not deserialize null to a primitive type"); |
} else { |
return wrapped.deserialize(reader, ctx); |
} |
} |
} |
// TODO check if making the delegate instance final would improve perfs |
private class NullConverterWrapper<T> extends Wrapper<Converter<T>> implements |
Converter<T> { |
private final T defaultValue; |
public NullConverterWrapper(T defaultValue, Converter<T> converter) { |
super(converter); |
this.defaultValue = defaultValue; |
} |
public void serialize(T obj, ObjectWriter writer, Context ctx) throws Exception { |
if (obj == null) { |
writer.writeNull(); |
} else { |
wrapped.serialize(obj, writer, ctx); |
} |
} |
public T deserialize(ObjectReader reader, Context ctx) throws Exception { |
if (ValueType.NULL == reader.getValueType()) { |
return defaultValue; |
} else { |
return wrapped.deserialize(reader, ctx); |
} |
} |
} |
@SuppressWarnings({"unchecked", "rawtypes"}) |
@Override |
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) { |
if (Wrapper.toAnnotatedElement(nextConverter).isAnnotationPresent(HandleNull.class)) { |
return nextConverter; |
} else { |
Class<?> rawClass = TypeUtil.getRawClass(type); |
if (failOnNullPrimitive && rawClass.isPrimitive()) { |
return new FailIfNullConverter(nextConverter); |
} else { |
return new NullConverterWrapper(genson.defaultValue(rawClass), nextConverter); |
} |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/RuntimeTypeConverter.java |
---|
New file |
0,0 → 1,50 |
package com.owlike.genson.convert; |
import java.lang.reflect.Type; |
import com.owlike.genson.Context; |
import com.owlike.genson.Converter; |
import com.owlike.genson.Genson; |
import com.owlike.genson.Wrapper; |
import com.owlike.genson.reflect.TypeUtil; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
/** |
* This converter will use the runtime type of objects during serialization. |
* |
* @param <T> the type this converter is handling. |
* @author eugen |
*/ |
public class RuntimeTypeConverter<T> extends Wrapper<Converter<T>> implements Converter<T> { |
public static class RuntimeTypeConverterFactory extends ChainedFactory { |
@SuppressWarnings({"unchecked", "rawtypes"}) |
@Override |
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) { |
if (nextConverter == null) |
throw new IllegalArgumentException( |
"RuntimeTypeConverter can not be last Converter in the chain."); |
return (Converter<?>) new RuntimeTypeConverter(TypeUtil.getRawClass(type), |
nextConverter); |
} |
} |
private final Class<T> tClass; |
public RuntimeTypeConverter(Class<T> tClass, Converter<T> next) { |
super(next); |
this.tClass = tClass; |
} |
public void serialize(T obj, ObjectWriter writer, Context ctx) throws Exception { |
if (obj != null && !tClass.equals(obj.getClass())) |
ctx.genson.serialize(obj, obj.getClass(), writer, ctx); |
else |
wrapped.serialize(obj, writer, ctx); |
} |
public T deserialize(ObjectReader reader, Context ctx) throws Exception { |
return wrapped.deserialize(reader, ctx); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/ClassMetadataConverter.java |
---|
New file |
0,0 → 1,104 |
package com.owlike.genson.convert; |
import java.lang.reflect.Type; |
import com.owlike.genson.*; |
import com.owlike.genson.annotation.HandleClassMetadata; |
import com.owlike.genson.convert.DefaultConverters.UntypedConverterFactory.UntypedConverter; |
import com.owlike.genson.reflect.TypeUtil; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
import com.owlike.genson.stream.ValueType; |
/** |
* Converter responsible of writing and reading @class metadata. This is useful if you want to be |
* able to deserialize all serialized objects without knowing their concrete type. Metadata is |
* written only in objects (never in arrays or literals) and is always the first element in the |
* object. Most default converters are annotated with @HandleClassMetada indicating that they will |
* not have class metadata written nor use it during deserialization. This feature is disabled by |
* default, to enable it use {@link com.owlike.genson.GensonBuilder#useClassMetadata(boolean)}. |
* Genson provides also a aliases mechanism that will replace the class name with the value of your alias |
* in the generated stream. You should use it as it is more "secure" and provides you more flexibility. |
* Indeed if you change the name or package of your class you will still be able to deserialize to it. |
* An example allowing to serialize a object and then deserialize it back without knowing its type would be: |
* <p/> |
* <pre> |
* class Foo { |
* } |
* |
* Genson genson = new GensonBuilder().useClassMetadata(true).addAlias("foo", Foo.class).create(); |
* String json = genson.serialize(new Foo()); |
* // json value will be {"@class":"Foo"} |
* Foo foo = (Foo) genson.deserialize(json, Object.class); |
* </pre> |
* |
* @param <T> |
* @author eugen |
* @see com.owlike.genson.stream.ObjectWriter#writeMetadata(String, String) ObjectWriter.metadata(key, value) |
* @see com.owlike.genson.stream.ObjectReader#metadata(String) ObjectReader.metadata("class") |
* @see com.owlike.genson.Genson#aliasFor(Class) Genson.aliasFor(Class) |
*/ |
public class ClassMetadataConverter<T> extends Wrapper<Converter<T>> implements Converter<T> { |
public static class ClassMetadataConverterFactory extends ChainedFactory { |
private final boolean classMetadataWithStaticType; |
public ClassMetadataConverterFactory(boolean classMetadataWithStaticType) { |
this.classMetadataWithStaticType = classMetadataWithStaticType; |
} |
@SuppressWarnings({"unchecked", "rawtypes"}) |
@Override |
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) { |
if (nextConverter == null) |
throw new IllegalArgumentException( |
"nextConverter must be not null for ClassMetadataConverter, " |
+ "as ClassMetadataConverter can not be the last converter in the chain!"); |
Class<?> rawClass = TypeUtil.getRawClass(type); |
if (genson.isWithClassMetadata() |
&& !Wrapper.toAnnotatedElement(nextConverter).isAnnotationPresent(HandleClassMetadata.class)) |
return new ClassMetadataConverter(rawClass, nextConverter, classMetadataWithStaticType); |
else |
return nextConverter; |
} |
} |
private final boolean classMetadataWithStaticType; |
private final Class<T> tClass; |
private final boolean skipMetadataSerialization; |
public ClassMetadataConverter(Class<T> tClass, Converter<T> delegate, boolean classMetadataWithStaticType) { |
super(delegate); |
this.tClass = tClass; |
this.classMetadataWithStaticType = classMetadataWithStaticType; |
skipMetadataSerialization = Wrapper.isOfType(delegate, UntypedConverter.class); |
} |
public void serialize(T obj, ObjectWriter writer, Context ctx) throws Exception { |
if (!skipMetadataSerialization && obj != null && |
(classMetadataWithStaticType || (!classMetadataWithStaticType && !tClass.equals(obj.getClass())))) { |
writer.beginNextObjectMetadata() |
.writeMetadata("class", ctx.genson.aliasFor(obj.getClass())); |
} |
wrapped.serialize(obj, writer, ctx); |
} |
public T deserialize(ObjectReader reader, Context ctx) throws Exception { |
if (ValueType.OBJECT.equals(reader.getValueType())) { |
String className = reader.nextObjectMetadata().metadata("class"); |
if (className != null) { |
try { |
Class<?> classFromMetadata = ctx.genson.classFor(className); |
if (!classFromMetadata.equals(tClass)) { |
Converter<T> deser = ctx.genson.provideConverter(classFromMetadata); |
return deser.deserialize(reader, ctx); |
} |
} catch (ClassNotFoundException e) { |
throw new JsonBindingException( |
"Could not use @class metadata, no such class: " + className); |
} |
} |
} |
return wrapped.deserialize(reader, ctx); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/convert/ContextualFactory.java |
---|
New file |
0,0 → 1,24 |
package com.owlike.genson.convert; |
import com.owlike.genson.Converter; |
import com.owlike.genson.Genson; |
import com.owlike.genson.reflect.BeanProperty; |
/** |
* <b>Beta feature</b> |
* <br/> |
* Create method signature and BeanProperty might change in the future. |
* Allows to create a converter for some type T based on bean property available at compile time |
* (ex: you can not use it with map keys because they exist only at runtime). This feature works |
* only for POJO databinding, in could be improved implying some refactoring. |
* |
* @param <T> the type of objects handled by Converters built by this factory |
* @author eugen |
*/ |
public interface ContextualFactory<T> { |
/** |
* Return an instance of a converter working with objects of type T based on property argument |
* or null. |
*/ |
public Converter<T> create(BeanProperty property, Genson genson); |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/BeanView.java |
---|
New file |
0,0 → 1,117 |
package com.owlike.genson; |
/** |
* Interface to be implemented by classes who want to act as a view on objects of type T during |
* serialization and deserializaiton. |
* <p/> |
* To understand what a BeanView is we must first understand one of the problems it is intended to |
* solve. Imagine you store some business objects in a cache and you have internal and external |
* webservices that all return a different json representation of those objects (filtered properties |
* and even transformed properties). The external webservices can't return the same representation |
* as the internal ones as the object may contain some confidential data. |
* <ul> |
* Usually you have two choices : |
* <li>Use a different instance of the json library for each different json representation and |
* configure it with custom Serializers/Deserializers (you can use annotations to filter properties |
* but not to change its name if you have multiple names and nor to transform the data).</li> |
* <li>Use data transfer objects that will act as a "View of your Model". You will have to copy the |
* data from the cached objects to the DTOs and serialize them. As a result your cache has lost some |
* of its interest (you will create new instances of DTOs).</li> |
* </ul> |
* <p/> |
* The BeanView tries to solve this kind of problem by taking the second approach. Indeed |
* implementations of BeanView will act as a stateless bean that will extract data (and could apply |
* transformations) during serialization and as a factory and data aggregator during |
* deserialization. The parameterized type T will correspond to the type of the objects on which |
* this view can be applied. All the methods from the view respecting the conventional JavaBean |
* structure will be used (getters to extract data, setters to aggregate and static methods |
* annotated with {@link com.owlike.genson.annotation.JsonCreator JsonCreator} as factory methods). Except that the |
* getters will take an argument of type T (from which to extract the data), and the setter two |
* arguments, the value (can be a complex object, in that case Genson will try to deserialize the |
* current value into that type) and T object in which to set the data. Parameters order matters, |
* for setters the first parameter is the value to deserialize and the second is the object that you |
* are building (of type T). By default the beanview functionality is disabled, to enable it use |
* method {@link com.owlike.genson.GensonBuilder#useBeanViews(boolean)} |
* setWithBeanViewConverter(true)} from Genson.Builder. Lets have a look at this example to better |
* understand how it works. |
* <p/> |
* <pre> |
* public static class Person { |
* private String lastName; |
* String name; |
* int birthYear; |
* String thisFieldWontBeSerialized; |
* |
* Person(String lastName) { |
* this.lastName = lastName; |
* } |
* |
* public String getLastName() { |
* return lastName; |
* } |
* |
* // instead of serializing and deserializing Person based on the fields and methods it contains those |
* // and only those from the BeanView will be used |
* public static class ViewOfPerson implements BeanView<Person> { |
* public ViewOfPerson() { |
* } |
* |
* // This method will be called to create an instance of Person instead of using the constructor |
* // or annotated @Creator method from Person |
* @JsonCreator |
* public static Person createNewPerson(String lastName) { |
* return new Person(lastName); |
* } |
* |
* public String getLastName(Person p) { |
* return p.getLastName(); |
* } |
* |
* public @JsonProperty("name") |
* String getNameOf(Person p) { |
* return p.name; |
* } |
* |
* // here we will transform the birth year of the person into its age and change the serialized |
* // name from "birthYear" to "age" |
* public int getAge(Person p) { |
* return GregorianCalendar.getInstance().get(Calendar.YEAR) - p.birthYear; |
* } |
* |
* public void setName(String name, Person p) { |
* p.name = name; |
* } |
* |
* // here it will match the property named "age" from the json stream and transform it into birth |
* // year of Person |
* @JsonProperty("age") |
* public void setBirthYear(int personBirthYear, Person p) { |
* p.birthYear = GregorianCalendar.getInstance().get(Calendar.YEAR) - personBirthYear; |
* } |
* } |
* |
* public static void main(String[] args) { |
* Genson genson = new Genson.Builder().setWithBeanViewConverter(true).create(); |
* genson.serialize(new Person("eugen"), ViewOfPerson.class); |
* } |
* </pre> |
* <p/> |
* <p/> |
* Implementations of BeanView must be stateless, thread safe and have a default no arg constructor. |
* BeanViews will be applied at <u>runtime before the standard Converter</u>. If a view for the |
* current type is present in the context it will be used instead of the corresponding Converter. If |
* you want to understand how it works behind the scene you can have a look at <a href= |
* "http://code.google.com/p/genson/source/browse/src/main/java/com/owlike/genson/convert/BeanViewConverter.java" |
* >BeanViewConverter</a> and <a href= |
* "http://code.google.com/p/genson/source/browse/src/main/java/com/owlike/genson/reflect/BeanViewDescriptorProvider.java" |
* >BeanViewDescriptorProvider</a>. |
* |
* @param <T> the type of objects on which this view will be applied. |
* @see com.owlike.genson.convert.BeanViewConverter BeanViewConverter |
* @see com.owlike.genson.reflect.BeanViewDescriptorProvider BeanViewDescriptorProvider |
* @see com.owlike.genson.annotation.JsonCreator JsonCreator |
* @see com.owlike.genson.annotation.JsonProperty JsonProperty |
*/ |
public interface BeanView<T> { |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/GensonBuilder.java |
---|
New file |
0,0 → 1,1014 |
package com.owlike.genson; |
import com.owlike.genson.convert.*; |
import com.owlike.genson.ext.GensonBundle; |
import com.owlike.genson.reflect.*; |
import com.owlike.genson.reflect.BeanDescriptorProvider.CompositeBeanDescriptorProvider; |
import com.owlike.genson.reflect.AbstractBeanDescriptorProvider.ContextualFactoryDecorator; |
import com.owlike.genson.reflect.AbstractBeanDescriptorProvider.ContextualConverterFactory; |
import java.lang.reflect.Type; |
import java.text.DateFormat; |
import java.text.SimpleDateFormat; |
import java.util.*; |
/** |
* Use the GensonBuilder class when you want to create a custom Genson instance. This class allows you |
* for example to register custom converters/serializers/deserializers |
* {@link #withConverters(Converter...)} or custom converter Factories |
* {@link #withConverterFactory(Factory)}. |
* <p/> |
* This class combines the GensonBuilder design pattern with template pattern providing handy |
* configuration and extensibility. All its public methods are intended to be used in the |
* GensonBuilder "style" and its protected methods are part of the template. When you call |
* {@link #create()} method, it will start assembling all the configuration and build all the |
* required components by using the protected methods. For example if you wish to use in your |
* projects a GensonBuilder that will always create some custom |
* {@link com.owlike.genson.reflect.BeanDescriptorProvider BeanDescriptorProvider} you have to |
* extend {@link #createBeanDescriptorProvider()}, or imagine that you implemented some |
* Converters that you always want to register then override {@link #getDefaultConverters()}. |
* |
* @author eugen |
*/ |
public class GensonBuilder { |
private final Map<Type, Serializer<?>> serializersMap = new HashMap<Type, Serializer<?>>(); |
private final Map<Type, Deserializer<?>> deserializersMap = new HashMap<Type, Deserializer<?>>(); |
private final List<Factory<?>> converterFactories = new ArrayList<Factory<?>>(); |
private final List<ContextualFactory<?>> contextualFactories = new ArrayList<ContextualFactory<?>>(); |
private final List<BeanPropertyFactory> beanPropertyFactories = new ArrayList<BeanPropertyFactory>(); |
private boolean skipNull = false; |
private boolean htmlSafe = false; |
private boolean withClassMetadata = false; |
private boolean throwExcOnNoDebugInfo = false; |
private boolean useGettersAndSetters = true; |
private boolean useFields = true; |
private boolean withBeanViewConverter = false; |
private boolean useRuntimeTypeForSerialization = false; |
private boolean withDebugInfoPropertyNameResolver = false; |
private boolean strictDoubleParse = false; |
private boolean indent = false; |
private boolean metadata = false; |
private boolean failOnMissingProperty = false; |
private List<GensonBundle> _bundles = new ArrayList<GensonBundle>(); |
private PropertyNameResolver propertyNameResolver; |
private BeanMutatorAccessorResolver mutatorAccessorResolver; |
private VisibilityFilter propertyFilter = VisibilityFilter.PACKAGE_PUBLIC; |
private VisibilityFilter methodFilter = VisibilityFilter.PACKAGE_PUBLIC; |
private VisibilityFilter constructorFilter = VisibilityFilter.PACKAGE_PUBLIC; |
private ClassLoader classLoader = getClass().getClassLoader(); |
private BeanDescriptorProvider beanDescriptorProvider; |
private DateFormat dateFormat = SimpleDateFormat.getDateTimeInstance(); |
private boolean useDateAsTimestamp = true; |
private boolean classMetadataWithStaticType = true; |
// for the moment we don't allow to override |
private BeanViewDescriptorProvider beanViewDescriptorProvider; |
private final Map<String, Class<?>> withClassAliases = new HashMap<String, Class<?>>(); |
private final Map<Class<?>, BeanView<?>> registeredViews = new HashMap<Class<?>, BeanView<?>>(); |
private ChainedFactory customFactoryChain; |
private final Map<Class<?>, Object> defaultValues = new HashMap<Class<?>, Object>(); |
private boolean failOnNullPrimitive = false; |
private RuntimePropertyFilter runtimePropertyFilter = RuntimePropertyFilter.noFilter; |
public GensonBuilder() { |
defaultValues.put(int.class, 0); |
defaultValues.put(long.class, 0l); |
defaultValues.put(short.class, (short) 0); |
defaultValues.put(double.class, 0d); |
defaultValues.put(float.class, 0f); |
defaultValues.put(boolean.class, false); |
defaultValues.put(byte.class, (byte) 0); |
defaultValues.put(char.class, '\u0000'); |
} |
/** |
* Alias used in serialized class metadata instead of the full class name. See |
* {@link com.owlike.genson.convert.ClassMetadataConverter ClassMetadataConverter} for more |
* metadata. If you add an alias, it will automatically enable the class metadata feature, |
* as if you used {@link #useClassMetadata(boolean)}. |
* |
* @param alias |
* @param forClass |
* @return a reference to this builder. |
*/ |
public GensonBuilder addAlias(String alias, Class<?> forClass) { |
withClassMetadata = true; |
withClassAliases.put(alias, forClass); |
return this; |
} |
/** |
* Registers converters mapping them to their corresponding parameterized type. |
* |
* @param converter |
* @return a reference to this builder. |
*/ |
public GensonBuilder withConverters(Converter<?>... converter) { |
for (Converter<?> c : converter) { |
Type typeOfConverter = TypeUtil.typeOf(0, |
TypeUtil.lookupGenericType(Converter.class, c.getClass())); |
typeOfConverter = TypeUtil.expandType(typeOfConverter, c.getClass()); |
registerConverter(c, typeOfConverter); |
} |
return this; |
} |
/** |
* Register converter by mapping it to type argument. |
* |
* @param converter to register |
* @param type of objects this converter handles |
* @return a reference to this builder. |
*/ |
public <T> GensonBuilder withConverter(Converter<T> converter, Class<? extends T> type) { |
registerConverter(converter, type); |
return this; |
} |
/** |
* Register converter by mapping it to the parameterized type of type argument. |
* |
* @param converter to register |
* @param type of objects this converter handles |
* @return a reference to this builder. |
*/ |
public <T> GensonBuilder withConverter(Converter<T> converter, GenericType<? extends T> type) { |
registerConverter(converter, type.getType()); |
return this; |
} |
private <T> void registerConverter(Converter<T> converter, Type type) { |
if (serializersMap.containsKey(type)) |
throw new IllegalStateException("Can not register converter " |
+ converter.getClass() |
+ ". A custom serializer is already registered for type " + type); |
if (deserializersMap.containsKey(type)) |
throw new IllegalStateException("Can not register converter " |
+ converter.getClass() |
+ ". A custom deserializer is already registered for type " + type); |
serializersMap.put(type, converter); |
deserializersMap.put(type, converter); |
} |
public GensonBuilder withSerializers(Serializer<?>... serializer) { |
for (Serializer<?> s : serializer) { |
Type typeOfConverter = TypeUtil.typeOf(0, |
TypeUtil.lookupGenericType(Serializer.class, s.getClass())); |
typeOfConverter = TypeUtil.expandType(typeOfConverter, s.getClass()); |
registerSerializer(s, typeOfConverter); |
} |
return this; |
} |
public <T> GensonBuilder withSerializer(Serializer<T> serializer, Class<? extends T> type) { |
registerSerializer(serializer, type); |
return this; |
} |
public <T> GensonBuilder withSerializer(Serializer<T> serializer, GenericType<? extends T> type) { |
registerSerializer(serializer, type.getType()); |
return this; |
} |
private <T> void registerSerializer(Serializer<T> serializer, Type type) { |
if (serializersMap.containsKey(type)) |
throw new IllegalStateException("Can not register serializer " |
+ serializer.getClass() |
+ ". A custom serializer is already registered for type " + type); |
serializersMap.put(type, serializer); |
} |
public GensonBuilder withDeserializers(Deserializer<?>... deserializer) { |
for (Deserializer<?> d : deserializer) { |
Type typeOfConverter = TypeUtil.typeOf(0, |
TypeUtil.lookupGenericType(Deserializer.class, d.getClass())); |
typeOfConverter = TypeUtil.expandType(typeOfConverter, d.getClass()); |
registerDeserializer(d, typeOfConverter); |
} |
return this; |
} |
public <T> GensonBuilder withDeserializer(Deserializer<T> deserializer, Class<? extends T> type) { |
registerDeserializer(deserializer, type); |
return this; |
} |
public <T> GensonBuilder withDeserializer(Deserializer<T> deserializer, |
GenericType<? extends T> type) { |
registerDeserializer(deserializer, type.getType()); |
return this; |
} |
private <T> void registerDeserializer(Deserializer<T> deserializer, Type type) { |
if (deserializersMap.containsKey(type)) |
throw new IllegalStateException("Can not register deserializer " |
+ deserializer.getClass() |
+ ". A custom deserializer is already registered for type " + type); |
deserializersMap.put(type, deserializer); |
} |
/** |
* Registers converter factories. |
* |
* @param factory to register |
* @return a reference to this builder. |
*/ |
public GensonBuilder withConverterFactory(Factory<? extends Converter<?>> factory) { |
converterFactories.add(factory); |
return this; |
} |
/** |
* Registers serializer factories. |
* |
* @param factory to register |
* @return a reference to this builder. |
*/ |
public GensonBuilder withSerializerFactory(Factory<? extends Serializer<?>> factory) { |
converterFactories.add(factory); |
return this; |
} |
/** |
* Registers deserializer factories. |
* |
* @param factory to register |
* @return a reference to this builder. |
*/ |
public GensonBuilder withDeserializerFactory(Factory<? extends Deserializer<?>> factory) { |
converterFactories.add(factory); |
return this; |
} |
/** |
* ContextualFactory is actually in a beta status, it will not be removed, but might be |
* refactored. |
*/ |
public GensonBuilder withContextualFactory(ContextualFactory<?>... factories) { |
contextualFactories.addAll(Arrays.asList(factories)); |
return this; |
} |
/** |
* A ChainedFactory provides a way to use custom Converters that have access to the default Converters. |
* An example of use is to wrap incoming/outgoing json in a root object and delegate then the ser/de to the default |
* Converter. |
* |
* This mechanism is internally used by Genson to decorate the different Converters with additional behaviour |
* (null handling, ser/de of polymorphic types with class info, runtime type based ser/de, etc). |
* |
* Note that you can't use it in situations where you want to start reading/writing some partial infos and want to |
* delegate the rest to the default Converter. |
*/ |
public GensonBuilder withConverterFactory(ChainedFactory chainedFactory) { |
if (customFactoryChain == null) customFactoryChain = chainedFactory; |
else { |
customFactoryChain.append(chainedFactory); |
} |
return this; |
} |
/** |
* Allows you to register new BeanPropertyFactory responsible of creating BeanProperty |
* accessors, mutators and BeanCreators. This is a very low level feature, you probably |
* don't need it. |
*/ |
public GensonBuilder withBeanPropertyFactory(BeanPropertyFactory... factories) { |
beanPropertyFactories.addAll(Arrays.asList(factories)); |
return this; |
} |
/** |
* Register some genson bundles. For example to enable JAXB support: |
* <p/> |
* <pre> |
* builder.withBundle(new JAXBExtension()); |
* </pre> |
* |
* <b>All bundles should be registered before any other customization.</b> |
* |
* @see GensonBundle |
*/ |
public GensonBuilder withBundle(GensonBundle... bundles) { |
for (GensonBundle bundle : bundles) { |
bundle.configure(this); |
_bundles.add(bundle); |
} |
return this; |
} |
/** |
* Override the default classloader |
* |
* @param loader classloader which will be used to load classes while deserializing |
* @return a reference to this builder |
*/ |
public GensonBuilder withClassLoader(ClassLoader loader) { |
classLoader = loader; |
return this; |
} |
/** |
* Replaces default {@link com.owlike.genson.reflect.BeanMutatorAccessorResolver |
* BeanMutatorAccessorResolver} by the specified one. |
* |
* @param resolver |
* @return a reference to this builder. |
*/ |
public GensonBuilder set(BeanMutatorAccessorResolver resolver) { |
mutatorAccessorResolver = resolver; |
return this; |
} |
/** |
* Replaces default {@link com.owlike.genson.reflect.PropertyNameResolver |
* PropertyNameResolver} by the specified one. |
* |
* @param resolver |
* @return a reference to this builder. |
*/ |
public GensonBuilder set(PropertyNameResolver resolver) { |
propertyNameResolver = resolver; |
return this; |
} |
/** |
* Register additional BeanMutatorAccessorResolver that will be used before the standard |
* ones. |
* |
* @param resolvers |
* @return a reference to this builder. |
*/ |
public GensonBuilder with(BeanMutatorAccessorResolver... resolvers) { |
if (mutatorAccessorResolver == null) |
mutatorAccessorResolver = createBeanMutatorAccessorResolver(); |
if (mutatorAccessorResolver instanceof BeanMutatorAccessorResolver.CompositeResolver) |
((BeanMutatorAccessorResolver.CompositeResolver) mutatorAccessorResolver).add(resolvers); |
else |
throw new IllegalStateException( |
"You can not add multiple resolvers if the base resolver is not of type " |
+ BeanMutatorAccessorResolver.CompositeResolver.class.getName()); |
return this; |
} |
public Map<Type, Serializer<?>> getSerializersMap() { |
return Collections.unmodifiableMap(serializersMap); |
} |
public Map<Type, Deserializer<?>> getDeserializersMap() { |
return Collections.unmodifiableMap(deserializersMap); |
} |
public ClassLoader getClassLoader() { |
return classLoader; |
} |
/** |
* Registers the specified resolvers in the order they were defined and before the standard |
* ones. |
* |
* @param resolvers |
* @return a reference to this builder. |
*/ |
public GensonBuilder with(PropertyNameResolver... resolvers) { |
if (propertyNameResolver == null) propertyNameResolver = createPropertyNameResolver(); |
if (propertyNameResolver instanceof PropertyNameResolver.CompositePropertyNameResolver) |
((PropertyNameResolver.CompositePropertyNameResolver) propertyNameResolver).add(resolvers); |
else |
throw new IllegalStateException( |
"You can not add multiple resolvers if the base resolver is not of type " |
+ PropertyNameResolver.CompositePropertyNameResolver.class.getName()); |
return this; |
} |
/** |
* Renames all fields named field to toName. |
*/ |
public GensonBuilder rename(String field, String toName) { |
return rename(field, null, toName, null); |
} |
/** |
* Renames all fields of type fieldOfType to toName. |
*/ |
public GensonBuilder rename(Class<?> fieldOfType, String toName) { |
return rename(null, null, toName, fieldOfType); |
} |
/** |
* Renames all fields named field declared in class fromClass to toName. |
*/ |
public GensonBuilder rename(String field, Class<?> fromClass, String toName) { |
return rename(field, fromClass, toName, null); |
} |
/** |
* Renames all fields named field and of type fieldOfType to toName. |
*/ |
public GensonBuilder rename(String field, String toName, Class<?> fieldOfType) { |
return rename(field, null, toName, fieldOfType); |
} |
/** |
* Renames all fields named field, of type fieldOfType and declared in fromClass to toName. |
*/ |
public GensonBuilder rename(final String field, final Class<?> fromClass, final String toName, |
final Class<?> ofType) { |
return with(new RenamingPropertyNameResolver(field, fromClass, ofType, toName)); |
} |
public GensonBuilder exclude(String field) { |
return filter(field, null, null, true); |
} |
public GensonBuilder exclude(Class<?> fieldOfType) { |
return filter(null, null, fieldOfType, true); |
} |
public GensonBuilder exclude(String field, Class<?> fromClass) { |
return filter(field, fromClass, null, true); |
} |
public GensonBuilder exclude(String field, Class<?> fromClass, Class<?> ofType) { |
return filter(field, fromClass, ofType, true); |
} |
public GensonBuilder include(String field) { |
return filter(field, null, null, false); |
} |
public GensonBuilder include(Class<?> fieldOfType) { |
return filter(null, null, fieldOfType, false); |
} |
public GensonBuilder include(String field, Class<?> fromClass) { |
return filter(field, fromClass, null, false); |
} |
public GensonBuilder include(String field, Class<?> fromClass, Class<?> ofType) { |
return filter(field, fromClass, ofType, false); |
} |
private GensonBuilder filter(final String field, final Class<?> declaringClass, |
final Class<?> ofType, final boolean exclude) { |
return with(new PropertyFilter(exclude, field, declaringClass, ofType)); |
} |
/** |
* If true will not serialize null values |
* |
* @param skipNull indicates whether null values should be serialized or not. |
* @return a reference to this builder. |
*/ |
public GensonBuilder setSkipNull(boolean skipNull) { |
this.skipNull = skipNull; |
return this; |
} |
public boolean isSkipNull() { |
return skipNull; |
} |
/** |
* If true \,<,>,&,= characters will be replaced by \u0027, \u003c, \u003e, \u0026, \u003d |
* |
* @param htmlSafe indicates whether serialized data should be html safe. |
* @return a reference to this builder. |
*/ |
public GensonBuilder setHtmlSafe(boolean htmlSafe) { |
this.htmlSafe = htmlSafe; |
return this; |
} |
public boolean isHtmlSafe() { |
return htmlSafe; |
} |
/** |
* Indicates whether class metadata should be serialized and used during deserialization. |
* |
* @see com.owlike.genson.convert.ClassMetadataConverter ClassMetadataConverter |
*/ |
public GensonBuilder useClassMetadata(boolean enabled) { |
this.withClassMetadata = enabled; |
this.metadata = true; |
return this; |
} |
/** |
* Specifies the data format that should be used for java.util.Date serialization and |
* deserialization. |
* |
* @param dateFormat |
* @return a reference to this builder. |
*/ |
public GensonBuilder useDateFormat(DateFormat dateFormat) { |
this.dateFormat = dateFormat; |
return this; |
} |
public boolean isThrowExceptionOnNoDebugInfo() { |
return throwExcOnNoDebugInfo; |
} |
/** |
* Used in conjunction with {@link #useConstructorWithArguments(boolean)}. If true |
* an exception will be thrown when a class has been compiled without debug informations. |
* |
* @param throwExcOnNoDebugInfo |
* @return a reference to this builder. |
* ASMCreatorParameterNameResolver |
*/ |
public GensonBuilder setThrowExceptionIfNoDebugInfo(boolean throwExcOnNoDebugInfo) { |
this.throwExcOnNoDebugInfo = throwExcOnNoDebugInfo; |
return this; |
} |
/** |
* If true, getters and setters would be used during serialization/deserialization in favor |
* of fields. If there is not getter/setter for a field then the field will be used, except |
* if you specified that fields should not be used with {@link #useFields(boolean)}. By |
* default getters, setters and fields will be used. |
*/ |
public GensonBuilder useMethods(boolean enabled) { |
this.useGettersAndSetters = enabled; |
return this; |
} |
public GensonBuilder useMethods(boolean enabled, VisibilityFilter visibility) { |
useMethods(enabled); |
return setMethodFilter(visibility); |
} |
/** |
* If true, fields will be used when no getter/setter is available, except if you specified |
* that no getter/setter should be used with {@link #useMethods(boolean)}, in |
* that case only fields will be used. By default getters, setters and fields will be used. |
*/ |
public GensonBuilder useFields(boolean enabled) { |
this.useFields = enabled; |
return this; |
} |
public GensonBuilder useFields(boolean enabled, VisibilityFilter visibility) { |
useFields(enabled); |
return setFieldFilter(visibility); |
} |
/** |
* If true {@link BeanView} mechanism will be enabled. |
*/ |
public GensonBuilder useBeanViews(boolean enabled) { |
this.withBeanViewConverter = enabled; |
return this; |
} |
/** |
* If true the concrete type of the serialized object will always be used. So if you have |
* List<Number> type it will not use the Number serializer but the one for the concrete type |
* of the current value. |
* |
* @param enabled |
* @return a reference to this builder. |
*/ |
public GensonBuilder useRuntimeType(boolean enabled) { |
this.useRuntimeTypeForSerialization = enabled; |
return this; |
} |
/** |
* If true constructor and method arguments name will be resolved from the generated debug |
* symbols during compilation. It is a very powerful feature from Genson, you should have a |
* ASMCreatorParameterNameResolver}. |
* |
* @param enabled |
* @return a reference to this builder. |
* @see #setThrowExceptionIfNoDebugInfo(boolean) |
*/ |
public GensonBuilder useConstructorWithArguments(boolean enabled) { |
this.withDebugInfoPropertyNameResolver = enabled; |
return this; |
} |
public GensonBuilder setFieldFilter(VisibilityFilter propertyFilter) { |
this.propertyFilter = propertyFilter; |
return this; |
} |
public GensonBuilder setMethodFilter(VisibilityFilter methodFilter) { |
this.methodFilter = methodFilter; |
return this; |
} |
public GensonBuilder setConstructorFilter(VisibilityFilter constructorFilter) { |
this.constructorFilter = constructorFilter; |
return this; |
} |
public GensonBuilder useStrictDoubleParse(boolean strictDoubleParse) { |
this.strictDoubleParse = strictDoubleParse; |
return this; |
} |
/** |
* If true outputed json will be indented using two spaces, otherwise (by default) all is |
* printed on same line. |
*/ |
public GensonBuilder useIndentation(boolean indent) { |
this.indent = indent; |
return this; |
} |
public GensonBuilder useDateAsTimestamp(boolean enabled) { |
this.useDateAsTimestamp = enabled; |
return this; |
} |
public GensonBuilder useMetadata(boolean metadata) { |
this.metadata = metadata; |
return this; |
} |
public GensonBuilder useByteAsInt(boolean enable) { |
if (enable) { |
withConverters(DefaultConverters.ByteArrayAsIntArrayConverter.instance); |
} |
return this; |
} |
/** |
* If set to true, Genson will throw a JsonBindingException when it encounters a property in the incoming json that does not match |
* a property in the class. |
* False by default. |
* @param enable |
* @return |
*/ |
public GensonBuilder failOnMissingProperty(boolean enable) { |
this.failOnMissingProperty = enable; |
return this; |
} |
/** |
* If set to false, during serialization class metadata will be serialized only for types where the runtime type differs from the static one. |
* Ex: |
* |
* <pre> |
* class Person { |
* public Address address; |
* } |
* </pre> |
* |
* Here if the concrete instance of address is Address then this type will not be serialized as metadata, but if they differ then |
* it is serialized. By default this option is true, all types are serialized. |
* @param enable |
* @return |
*/ |
public GensonBuilder useClassMetadataWithStaticType(boolean enable) { |
this.classMetadataWithStaticType = enable; |
return this; |
} |
/** |
* Wrap a single value into a list when a list is expected. Useful when dealing with APIs that unwrap |
* arrays containing a single value. Disabled by default. |
*/ |
public GensonBuilder acceptSingleValueAsList(boolean enable) { |
if (enable) withConverterFactory(DefaultConverters.SingleValueAsListFactory.instance); |
return this; |
} |
/** |
* Uses the passed value as the default value for this type. |
*/ |
public GensonBuilder useDefaultValue(Object value, Class<?> targetType) { |
defaultValues.put(targetType, value); |
return this; |
} |
/** |
* Will wrap all the root objects under outputKey during serializaiton and unwrap the content under |
* inputKey during deserializaiton. For example: |
* |
* <code> |
* Genson genson = new GensonBuilder().wrapRootValues("request", "response").create(); |
* |
* // would produce: {"response": {... person properties ...}} |
* genson.serialize(person); |
* |
* Person p = genson.deserialize("{\"request\":{...}}", Person.class); |
* </code> |
* |
* If you need this mechanism only for some types or using different root keys, then you can register JaxbBundle with |
* wrapRootValues(true) and annotate the specific classes with XmlRootElement. |
*/ |
public GensonBuilder wrapRootValues(final String inputKey, final String outputKey) { |
return withConverterFactory(new ChainedFactory() { |
@Override |
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) { |
return new DefaultConverters.WrappedRootValueConverter<Object>( |
inputKey, |
outputKey, |
(Converter<Object>) nextConverter |
); |
} |
}); |
} |
/** |
* False by default. When enabled a JsonBindingException will be thrown if null is encountered during serialization |
* (should never happen) or deserialization for a primitive type. |
*/ |
public GensonBuilder failOnNullPrimitive(boolean enabled) { |
this.failOnNullPrimitive = enabled; |
return this; |
} |
public GensonBuilder useRuntimePropertyFilter(RuntimePropertyFilter filter) { |
this.runtimePropertyFilter = filter; |
return this; |
} |
/** |
* Creates an instance of Genson. You may use this method as many times you want. It wont |
* change the state of the builder, in sense that the returned instance will have always the |
* same configuration. |
* |
* @return a new instance of Genson built for the current configuration. |
*/ |
public Genson create() { |
if (propertyNameResolver == null) propertyNameResolver = createPropertyNameResolver(); |
if (mutatorAccessorResolver == null) |
mutatorAccessorResolver = createBeanMutatorAccessorResolver(); |
List<Converter<?>> converters = getDefaultConverters(); |
addDefaultSerializers(converters); |
addDefaultDeserializers(converters); |
addDefaultSerializers(getDefaultSerializers()); |
addDefaultDeserializers(getDefaultDeserializers()); |
List<Factory<? extends Converter<?>>> convFactories = new ArrayList<Factory<? extends Converter<?>>>(); |
addDefaultConverterFactories(convFactories); |
converterFactories.addAll(convFactories); |
List<Factory<? extends Serializer<?>>> serializerFactories = new ArrayList<Factory<? extends Serializer<?>>>(); |
addDefaultSerializerFactories(serializerFactories); |
converterFactories.addAll(serializerFactories); |
List<Factory<? extends Deserializer<?>>> deserializerFactories = new ArrayList<Factory<? extends Deserializer<?>>>(); |
addDefaultDeserializerFactories(deserializerFactories); |
converterFactories.addAll(deserializerFactories); |
List<ContextualFactory<?>> defaultContextualFactories = new ArrayList<ContextualFactory<?>>(); |
addDefaultContextualFactories(defaultContextualFactories); |
contextualFactories.addAll(defaultContextualFactories); |
beanDescriptorProvider = createBeanDescriptorProvider(); |
if (withBeanViewConverter) { |
List<BeanMutatorAccessorResolver> resolvers = new ArrayList<BeanMutatorAccessorResolver>(); |
resolvers.add(new BeanViewDescriptorProvider.BeanViewMutatorAccessorResolver()); |
resolvers.add(mutatorAccessorResolver); |
beanViewDescriptorProvider = new BeanViewDescriptorProvider( |
new AbstractBeanDescriptorProvider.ContextualConverterFactory(contextualFactories), registeredViews, |
createBeanPropertyFactory(), |
new BeanMutatorAccessorResolver.CompositeResolver(resolvers), getPropertyNameResolver() |
); |
} |
return create(createConverterFactory(), withClassAliases); |
} |
private void addDefaultSerializers(List<? extends Serializer<?>> serializers) { |
if (serializers != null) { |
for (Serializer<?> serializer : serializers) { |
Type typeOfConverter = TypeUtil.typeOf(0, |
TypeUtil.lookupGenericType(Serializer.class, serializer.getClass())); |
typeOfConverter = TypeUtil.expandType(typeOfConverter, serializer.getClass()); |
if (!serializersMap.containsKey(typeOfConverter)) |
serializersMap.put(typeOfConverter, serializer); |
} |
} |
} |
private void addDefaultDeserializers(List<? extends Deserializer<?>> deserializers) { |
if (deserializers != null) { |
for (Deserializer<?> deserializer : deserializers) { |
Type typeOfConverter = TypeUtil.typeOf(0, TypeUtil.lookupGenericType(Deserializer.class, deserializer.getClass())); |
typeOfConverter = TypeUtil.expandType(typeOfConverter, deserializer.getClass()); |
if (!deserializersMap.containsKey(typeOfConverter)) |
deserializersMap.put(typeOfConverter, deserializer); |
} |
} |
} |
/** |
* In theory this allows you to extend Genson class and to instantiate it, but actually you |
* can not do it as Genson class is final. If some uses cases are discovered it may change. |
* |
* @param converterFactory |
* @param classAliases |
* @return a new Genson instance. |
*/ |
protected Genson create(Factory<Converter<?>> converterFactory, |
Map<String, Class<?>> classAliases) { |
return new Genson(converterFactory, getBeanDescriptorProvider(), |
isSkipNull(), isHtmlSafe(), classAliases, withClassMetadata, |
strictDoubleParse, indent, metadata, failOnMissingProperty, defaultValues, runtimePropertyFilter); |
} |
/** |
* You should override this method if you want to add custom |
* {@link com.owlike.genson.convert.ChainedFactory ChainedFactory} or if you need to chain |
* them differently. |
* |
* @return the converter <u>factory instance that will be used to resolve |
* <strong>ALL</strong> converters</u>. |
*/ |
protected Factory<Converter<?>> createConverterFactory() { |
ChainedFactory chainHead = new CircularClassReferenceConverterFactory(); |
chainHead.append(new NullConverterFactory(failOnNullPrimitive)); |
if (useRuntimeTypeForSerialization) chainHead.append(new RuntimeTypeConverter.RuntimeTypeConverterFactory()); |
chainHead.append(new ClassMetadataConverter.ClassMetadataConverterFactory(classMetadataWithStaticType)); |
if (customFactoryChain != null) chainHead.append(customFactoryChain); |
if (withBeanViewConverter) chainHead.append(new BeanViewConverter.BeanViewConverterFactory( |
getBeanViewDescriptorProvider())); |
ContextualFactoryDecorator ctxFactoryDecorator = new ContextualFactoryDecorator( |
new BasicConvertersFactory(getSerializersMap(), getDeserializersMap(), |
getFactories(), getBeanDescriptorProvider())); |
chainHead.append(ctxFactoryDecorator); |
return chainHead; |
} |
protected BeanMutatorAccessorResolver createBeanMutatorAccessorResolver() { |
List<BeanMutatorAccessorResolver> resolvers = new ArrayList<BeanMutatorAccessorResolver>(); |
resolvers.add(new BeanMutatorAccessorResolver.GensonAnnotationsResolver()); |
resolvers.add(new BeanMutatorAccessorResolver.StandardMutaAccessorResolver(propertyFilter, |
methodFilter, constructorFilter)); |
return new BeanMutatorAccessorResolver.CompositeResolver(resolvers); |
} |
/** |
* You can override this method if you want to change the |
* {@link com.owlike.genson.reflect.PropertyNameResolver PropertyNameResolver} that are |
* registered by default. You can also simply replace the default PropertyNameResolver by |
* setting another one with {@link #set(PropertyNameResolver)}. |
* |
* @return the property name resolver to be used. It should be an instance of |
* {@link com.owlike.genson.reflect.PropertyNameResolver.CompositePropertyNameResolver |
* PropertyNameResolver.CompositePropertyNameResolver}, otherwise you will not be |
* able to add others PropertyNameResolvers using |
* {@link #with(PropertyNameResolver...)} method. |
*/ |
protected PropertyNameResolver createPropertyNameResolver() { |
List<PropertyNameResolver> resolvers = new ArrayList<PropertyNameResolver>(); |
resolvers.add(new PropertyNameResolver.AnnotationPropertyNameResolver()); |
resolvers.add(new PropertyNameResolver.ConventionalBeanPropertyNameResolver()); |
return new PropertyNameResolver.CompositePropertyNameResolver(resolvers); |
} |
/** |
* You can override this methods if you want to change the default converters (remove some, |
* change the order, etc). |
* |
* @return the default converters list, must be not null. |
*/ |
protected List<Converter<?>> getDefaultConverters() { |
List<Converter<?>> converters = new ArrayList<Converter<?>>(); |
converters.add(DefaultConverters.StringConverter.instance); |
converters.add(DefaultConverters.NumberConverter.instance); |
converters.add(new DefaultConverters.DateConverter(dateFormat, useDateAsTimestamp)); |
converters.add(DefaultConverters.URLConverter.instance); |
converters.add(DefaultConverters.URIConverter.instance); |
converters.add(DefaultConverters.TimestampConverter.instance); |
converters.add(DefaultConverters.BigDecimalConverter.instance); |
converters.add(DefaultConverters.BigIntegerConverter.instance); |
converters.add(DefaultConverters.UUIDConverter.instance); |
converters.add(DefaultConverters.FileConverter.instance); |
return converters; |
} |
/** |
* Override this method if you want to change the default converter factories. |
* |
* @param factories list, is not null. |
*/ |
protected void addDefaultConverterFactories(List<Factory<? extends Converter<?>>> factories) { |
factories.add(DefaultConverters.ArrayConverterFactory.instance); |
factories.add(DefaultConverters.CollectionConverterFactory.instance); |
factories.add(DefaultConverters.MapConverterFactory.instance); |
factories.add(DefaultConverters.EnumConverterFactory.instance); |
factories.add(DefaultConverters.PrimitiveConverterFactory.instance); |
factories.add(DefaultConverters.UntypedConverterFactory.instance); |
factories.add(new DefaultConverters.CalendarConverterFactory( |
new DefaultConverters.DateConverter(dateFormat, useDateAsTimestamp) |
)); |
} |
protected void addDefaultContextualFactories(List<ContextualFactory<?>> factories) { |
factories.add(new DefaultConverters.DateContextualFactory()); |
factories.add(new DefaultConverters.PropertyConverterFactory()); |
} |
protected List<Serializer<?>> getDefaultSerializers() { |
return null; |
} |
protected void addDefaultSerializerFactories( |
List<Factory<? extends Serializer<?>>> serializerFactories) { |
} |
protected List<Deserializer<?>> getDefaultDeserializers() { |
return null; |
} |
protected void addDefaultDeserializerFactories( |
List<Factory<? extends Deserializer<?>>> deserializerFactories) { |
} |
/** |
* Creates the standard BeanDescriptorProvider that will be used to provide |
* {@link com.owlike.genson.reflect.BeanDescriptor BeanDescriptor} instances for |
* serialization/deserialization of all types that couldn't be handled by standard and |
* custom converters and converter factories. |
* |
* @return the BeanDescriptorProvider instance. |
*/ |
protected BeanDescriptorProvider createBeanDescriptorProvider() { |
ContextualConverterFactory contextualConverterFactory = new ContextualConverterFactory(contextualFactories); |
BeanPropertyFactory beanPropertyFactory = createBeanPropertyFactory(); |
List<BeanDescriptorProvider> providers = new ArrayList<BeanDescriptorProvider>(); |
for (GensonBundle bundle : _bundles) { |
BeanDescriptorProvider provider = bundle.createBeanDescriptorProvider(contextualConverterFactory, |
beanPropertyFactory, |
getMutatorAccessorResolver(), |
getPropertyNameResolver(), this); |
if (provider != null) providers.add(provider); |
} |
providers.add(new BaseBeanDescriptorProvider( |
new AbstractBeanDescriptorProvider.ContextualConverterFactory(contextualFactories), |
createBeanPropertyFactory(), getMutatorAccessorResolver(), getPropertyNameResolver(), |
useGettersAndSetters, useFields, true |
)); |
return new CompositeBeanDescriptorProvider(providers); |
} |
protected BeanPropertyFactory createBeanPropertyFactory() { |
if (withBeanViewConverter) |
beanPropertyFactories.add(new BeanViewDescriptorProvider.BeanViewPropertyFactory( |
registeredViews)); |
beanPropertyFactories.add(new BeanPropertyFactory.StandardFactory()); |
return new BeanPropertyFactory.CompositeFactory(beanPropertyFactories); |
} |
protected final PropertyNameResolver getPropertyNameResolver() { |
return propertyNameResolver; |
} |
protected final BeanMutatorAccessorResolver getMutatorAccessorResolver() { |
return mutatorAccessorResolver; |
} |
protected final BeanDescriptorProvider getBeanDescriptorProvider() { |
return beanDescriptorProvider; |
} |
protected final BeanViewDescriptorProvider getBeanViewDescriptorProvider() { |
return beanViewDescriptorProvider; |
} |
public final List<Factory<?>> getFactories() { |
return Collections.unmodifiableList(converterFactories); |
} |
public final boolean isDateAsTimestamp() { |
return useDateAsTimestamp; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/PropertyMutator.java |
---|
New file |
0,0 → 1,120 |
package com.owlike.genson.reflect; |
import java.lang.annotation.Annotation; |
import java.lang.reflect.Field; |
import java.lang.reflect.InvocationTargetException; |
import java.lang.reflect.Method; |
import java.lang.reflect.Type; |
import com.owlike.genson.*; |
import com.owlike.genson.stream.ObjectReader; |
public abstract class PropertyMutator extends BeanProperty implements Comparable<PropertyMutator> { |
Deserializer<Object> propertyDeserializer; |
protected PropertyMutator(String name, Type type, Class<?> declaringClass, Class<?> concreteClass, |
Annotation[] annotations, int modifiers) { |
super(name, type, declaringClass, concreteClass, annotations, modifiers); |
} |
public Object deserialize(ObjectReader reader, Context ctx) { |
try { |
return propertyDeserializer.deserialize(reader, ctx); |
} catch (Throwable th) { |
throw couldNotDeserialize(th); |
} |
} |
public void deserialize(Object into, ObjectReader reader, Context ctx) { |
Object propValue = null; |
try { |
propValue = propertyDeserializer.deserialize(reader, ctx); |
} catch (Throwable th) { |
throw couldNotDeserialize(th); |
} |
mutate(into, propValue); |
} |
public abstract void mutate(Object target, Object value); |
public int compareTo(PropertyMutator o) { |
return o.priority() - priority(); |
} |
protected JsonBindingException couldNotMutate(Exception e) { |
return new JsonBindingException("Could not mutate value of property named '" |
+ name + "' using mutator " + signature(), e); |
} |
protected JsonBindingException couldNotDeserialize(Throwable e) { |
return new JsonBindingException("Could not deserialize to property '" + name + "' of class " + declaringClass, e); |
} |
public static class MethodMutator extends PropertyMutator { |
protected final Method _setter; |
public MethodMutator(String name, Method setter, Type type, Class<?> concreteClass) { |
super(name, type, setter.getDeclaringClass(), concreteClass, setter.getAnnotations(), setter.getModifiers()); |
this._setter = setter; |
if (!_setter.isAccessible()) { |
_setter.setAccessible(true); |
} |
} |
@Override |
public void mutate(Object target, Object value) { |
try { |
_setter.invoke(target, value); |
} catch (IllegalArgumentException e) { |
throw couldNotMutate(e); |
} catch (IllegalAccessException e) { |
throw couldNotMutate(e); |
} catch (InvocationTargetException e) { |
throw couldNotMutate(e); |
} |
} |
@Override |
public String signature() { |
return _setter.toGenericString(); |
} |
@Override |
public int priority() { |
return 100; |
} |
} |
public static class FieldMutator extends PropertyMutator { |
protected final Field _field; |
public FieldMutator(String name, Field field, Type type, Class<?> concreteClass) { |
super(name, type, field.getDeclaringClass(), concreteClass, field.getAnnotations(), field.getModifiers()); |
this._field = field; |
if (!_field.isAccessible()) { |
_field.setAccessible(true); |
} |
} |
@Override |
public void mutate(Object target, Object value) { |
try { |
_field.set(target, value); |
} catch (IllegalArgumentException e) { |
throw couldNotMutate(e); |
} catch (IllegalAccessException e) { |
throw couldNotMutate(e); |
} |
} |
@Override |
public String signature() { |
return _field.toGenericString(); |
} |
@Override |
public int priority() { |
return 0; |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/BeanPropertyFactory.java |
---|
New file |
0,0 → 1,142 |
package com.owlike.genson.reflect; |
import static com.owlike.genson.reflect.TypeUtil.getRawClass; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.Field; |
import java.lang.reflect.Method; |
import java.lang.reflect.Type; |
import java.util.ArrayList; |
import java.util.List; |
import com.owlike.genson.Genson; |
public interface BeanPropertyFactory { |
PropertyAccessor createAccessor(String name, Field field, Type ofType, Genson genson); |
PropertyAccessor createAccessor(String name, Method method, Type ofType, Genson genson); |
BeanCreator createCreator(Type ofType, Constructor<?> ctr, String[] resolvedNames, |
Genson genson); |
BeanCreator createCreator(Type ofType, Method method, String[] resolvedNames, |
Genson genson); |
PropertyMutator createMutator(String name, Field field, Type ofType, Genson genson); |
PropertyMutator createMutator(String name, Method method, Type ofType, Genson genson); |
class CompositeFactory implements BeanPropertyFactory { |
private final List<BeanPropertyFactory> factories; |
public CompositeFactory(List<? extends BeanPropertyFactory> factories) { |
this.factories = new ArrayList<BeanPropertyFactory>(factories); |
} |
@Override |
public PropertyAccessor createAccessor(String name, Field field, Type ofType, Genson genson) { |
for (BeanPropertyFactory factory : factories) { |
PropertyAccessor accessor = factory.createAccessor(name, field, ofType, genson); |
if (accessor != null) return accessor; |
} |
throw new RuntimeException("Failed to create a accessor for field " + field); |
} |
@Override |
public PropertyAccessor createAccessor(String name, Method method, Type ofType, |
Genson genson) { |
for (BeanPropertyFactory factory : factories) { |
PropertyAccessor accessor = factory.createAccessor(name, method, ofType, genson); |
if (accessor != null) return accessor; |
} |
throw new RuntimeException("Failed to create a accessor for method " + method); |
} |
@Override |
public BeanCreator createCreator(Type ofType, Constructor<?> ctr, String[] resolvedNames, |
Genson genson) { |
for (BeanPropertyFactory factory : factories) { |
BeanCreator creator = factory.createCreator(ofType, ctr, resolvedNames, genson); |
if (creator != null) return creator; |
} |
throw new RuntimeException("Failed to create a BeanCreator for constructor " + ctr); |
} |
@Override |
public BeanCreator createCreator(Type ofType, Method method, String[] resolvedNames, |
Genson genson) { |
for (BeanPropertyFactory factory : factories) { |
BeanCreator creator = factory.createCreator(ofType, method, resolvedNames, genson); |
if (creator != null) return creator; |
} |
throw new RuntimeException("Failed to create a BeanCreator for method " + method); |
} |
@Override |
public PropertyMutator createMutator(String name, Field field, Type ofType, Genson genson) { |
for (BeanPropertyFactory factory : factories) { |
PropertyMutator mutator = factory.createMutator(name, field, ofType, genson); |
if (mutator != null) return mutator; |
} |
throw new RuntimeException("Failed to create a mutator for field " + field); |
} |
@Override |
public PropertyMutator createMutator(String name, Method method, Type ofType, Genson genson) { |
for (BeanPropertyFactory factory : factories) { |
PropertyMutator mutator = factory.createMutator(name, method, ofType, genson); |
if (mutator != null) return mutator; |
} |
throw new RuntimeException("Failed to create a mutator for method " + method); |
} |
} |
class StandardFactory implements BeanPropertyFactory { |
public PropertyAccessor createAccessor(String name, Field field, Type ofType, Genson genson) { |
Class<?> ofClass = getRawClass(ofType); |
Type expandedType = TypeUtil.expandType(field.getGenericType(), ofType); |
return new PropertyAccessor.FieldAccessor(name, field, expandedType, ofClass); |
} |
public PropertyAccessor createAccessor(String name, Method method, Type ofType, |
Genson genson) { |
Type expandedType = TypeUtil.expandType(method.getGenericReturnType(), ofType); |
return new PropertyAccessor.MethodAccessor(name, method, expandedType, |
getRawClass(ofType)); |
} |
public PropertyMutator createMutator(String name, Field field, Type ofType, Genson genson) { |
Class<?> ofClass = getRawClass(ofType); |
Type expandedType = TypeUtil.expandType(field.getGenericType(), ofType); |
return new PropertyMutator.FieldMutator(name, field, expandedType, ofClass); |
} |
public PropertyMutator createMutator(String name, Method method, Type ofType, Genson genson) { |
Type expandedType = TypeUtil.expandType(method.getGenericParameterTypes()[0], ofType); |
return new PropertyMutator.MethodMutator(name, method, expandedType, |
getRawClass(ofType)); |
} |
// ofClass is not necessarily of same type as method return type, as ofClass corresponds to |
// the declaring class! |
public BeanCreator createCreator(Type ofType, Method method, String[] resolvedNames, |
Genson genson) { |
return new BeanCreator.MethodBeanCreator(method, resolvedNames, expandTypes( |
method.getGenericParameterTypes(), ofType), getRawClass(ofType)); |
} |
public BeanCreator createCreator(Type ofType, Constructor<?> ctr, String[] resolvedNames, |
Genson genson) { |
return new BeanCreator.ConstructorBeanCreator(getRawClass(ofType), ctr, resolvedNames, |
expandTypes(ctr.getGenericParameterTypes(), ofType)); |
} |
public Type[] expandTypes(Type[] typesToExpand, Type inContext) { |
Type[] expandedTypes = new Type[typesToExpand.length]; |
for (int i = 0; i < typesToExpand.length; i++) { |
expandedTypes[i] = TypeUtil.expandType(typesToExpand[i], inContext); |
} |
return expandedTypes; |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/AbstractBeanDescriptorProvider.java |
---|
New file |
0,0 → 1,272 |
package com.owlike.genson.reflect; |
import java.lang.reflect.Type; |
import java.util.ArrayList; |
import java.util.HashMap; |
import java.util.Iterator; |
import java.util.LinkedHashMap; |
import java.util.LinkedList; |
import java.util.List; |
import java.util.Map; |
import com.owlike.genson.Converter; |
import com.owlike.genson.Factory; |
import com.owlike.genson.Genson; |
import com.owlike.genson.ThreadLocalHolder; |
import com.owlike.genson.convert.ContextualFactory; |
import static com.owlike.genson.reflect.TypeUtil.*; |
/** |
* Abstract implementation of {@link BeanDescriptorProvider} applying the template pattern. |
* <p/> |
* If you wonder how to implement the different abstract methods defined in this class have a look |
* at the <a href= |
* "http://code.google.com/p/genson/source/browse/src/main/java/com/owlike/genson/reflect/BaseBeanDescriptorProvider.java" |
* >BaseBeanDescriptorProvider</a>. |
* |
* @author eugen |
*/ |
public abstract class AbstractBeanDescriptorProvider implements BeanDescriptorProvider { |
final static String CONTEXT_KEY = "__GENSON$CREATION_CONTEXT"; |
final static String DO_NOT_CACHE_CONVERTER_KEY = "__GENSON$DO_NOT_CACHE_CONVERTER"; |
public final static class ContextualConverterFactory { |
private final List<? extends ContextualFactory<?>> contextualFactories; |
public ContextualConverterFactory(List<? extends ContextualFactory<?>> contextualFactories) { |
this.contextualFactories = contextualFactories != null ? new ArrayList<ContextualFactory<?>>( |
contextualFactories) : new ArrayList<ContextualFactory<?>>(); |
} |
Converter<?> provide(BeanProperty property, Genson genson) { |
Type type = property.getType(); |
for (Iterator<? extends ContextualFactory<?>> it = contextualFactories.iterator(); it |
.hasNext(); ) { |
ContextualFactory<?> factory = it.next(); |
Converter<?> object = null; |
Type factoryType = lookupGenericType(ContextualFactory.class, factory.getClass()); |
factoryType = expandType(factoryType, factory.getClass()); |
Type factoryParameter = typeOf(0, factoryType); |
if (type instanceof Class<?> && ((Class<?>) type).isPrimitive()) |
type = wrap((Class<?>) type); |
if (match(type, factoryParameter, false) |
&& (object = factory.create(property, genson)) != null) { |
return object; |
} |
} |
return null; |
} |
} |
public final static class ContextualFactoryDecorator implements Factory<Converter<?>> { |
private final Factory<Converter<?>> delegatedFactory; |
public ContextualFactoryDecorator(Factory<Converter<?>> delegatedFactory) { |
this.delegatedFactory = delegatedFactory; |
} |
@Override |
public Converter<?> create(Type type, Genson genson) { |
Converter<?> converter = ThreadLocalHolder.get(CONTEXT_KEY, Converter.class); |
if (converter != null) return converter; |
return delegatedFactory.create(type, genson); |
} |
} |
private final ContextualConverterFactory contextualConverterFactory; |
protected AbstractBeanDescriptorProvider(ContextualConverterFactory contextualConverterFactory) { |
this.contextualConverterFactory = contextualConverterFactory; |
} |
@Override |
public <T> BeanDescriptor<T> provide(Class<T> type, Genson genson) { |
return provide(type, type, genson); |
} |
@Override |
public <T> BeanDescriptor<T> provide(Class<T> ofClass, Type ofType, Genson genson) { |
Map<String, LinkedList<PropertyMutator>> mutatorsMap = new LinkedHashMap<String, LinkedList<PropertyMutator>>(); |
Map<String, LinkedList<PropertyAccessor>> accessorsMap = new LinkedHashMap<String, LinkedList<PropertyAccessor>>(); |
List<BeanCreator> creators = provideBeanCreators(ofType, genson); |
provideBeanPropertyAccessors(ofType, accessorsMap, genson); |
provideBeanPropertyMutators(ofType, mutatorsMap, genson); |
List<PropertyAccessor> accessors = new ArrayList<PropertyAccessor>(accessorsMap.size()); |
for (Map.Entry<String, LinkedList<PropertyAccessor>> entry : accessorsMap.entrySet()) { |
PropertyAccessor accessor = checkAndMergeAccessors(entry.getKey(), entry.getValue()); |
// in case of... |
if (accessor != null) accessors.add(accessor); |
} |
Map<String, PropertyMutator> mutators = new HashMap<String, PropertyMutator>(mutatorsMap.size()); |
for (Map.Entry<String, LinkedList<PropertyMutator>> entry : mutatorsMap.entrySet()) { |
PropertyMutator mutator = checkAndMergeMutators(entry.getKey(), entry.getValue()); |
if (mutator != null) mutators.put(mutator.name, mutator); |
} |
BeanCreator ctr = checkAndMerge(ofType, creators); |
if (ctr != null) mergeAccessorsWithCreatorProperties(ofType, accessors, ctr); |
if (ctr != null) mergeMutatorsWithCreatorProperties(ofType, mutators, ctr); |
// 1 - prepare the converters for the accessors |
for (PropertyAccessor accessor : accessors) { |
accessor.propertySerializer = provide(accessor, genson); |
} |
// 2 - prepare the mutators |
for (PropertyMutator mutator : mutators.values()) { |
mutator.propertyDeserializer = provide(mutator, genson); |
} |
// 3 - prepare the converters for creator parameters |
if (ctr != null) { |
for (PropertyMutator mutator : ctr.parameters.values()) { |
mutator.propertyDeserializer = provide(mutator, genson); |
} |
} |
for (PropertyMutator p : mutators.values()) { |
for (String alias : p.aliases()) mutators.put(alias, p); |
} |
// lets fail fast if the BeanDescriptor has been built for the wrong type. |
// another option could be to pass in all the methods an additional parameter Class<T> that |
// would not necessarily correspond to the rawClass of ofType. In fact we authorize that |
// ofType rawClass is different from Class<T>, but the BeanDescriptor must match! |
BeanDescriptor<T> descriptor = create(ofClass, ofType, ctr, accessors, mutators, genson); |
if (!ofClass.isAssignableFrom(descriptor.getOfClass())) |
throw new ClassCastException("Actual implementation of BeanDescriptorProvider " |
+ getClass() |
+ " seems to do something wrong. Expected BeanDescriptor for type " + ofClass |
+ " but provided BeanDescriptor for type " + descriptor.getOfClass()); |
return descriptor; |
} |
private Converter<Object> provide(BeanProperty property, Genson genson) { |
// contextual converters must not be retrieved from cache nor stored in cache, by first |
// trying to create it and reusing it during the |
// call to genson.provideConverter we avoid retrieving it from cache, and by setting |
// DO_NOT_CACHE_CONVERTER to true we tell genson not to store |
// this converter in cache |
@SuppressWarnings("unchecked") |
Converter<Object> converter = (Converter<Object>) contextualConverterFactory.provide( |
property, genson); |
if (converter != null) { |
ThreadLocalHolder.store(DO_NOT_CACHE_CONVERTER_KEY, true); |
ThreadLocalHolder.store(CONTEXT_KEY, converter); |
} |
try { |
return genson.provideConverter(property.type); |
} finally { |
if (converter != null) { |
ThreadLocalHolder.remove(DO_NOT_CACHE_CONVERTER_KEY, Boolean.class); |
ThreadLocalHolder.remove(CONTEXT_KEY, Converter.class); |
} |
} |
} |
/** |
* Creates an instance of BeanDescriptor based on the passed arguments. Subclasses can override |
* this method to create their own BeanDescriptors. |
* |
* @param forClass |
* @param ofType |
* @param creator |
* @param accessors |
* @param mutators |
* @return a instance |
*/ |
protected <T> BeanDescriptor<T> create(Class<T> forClass, Type ofType, BeanCreator creator, |
List<PropertyAccessor> accessors, Map<String, PropertyMutator> mutators, |
Genson genson) { |
return new BeanDescriptor<T>(forClass, getRawClass(ofType), accessors, mutators, creator, genson.failOnMissingProperty()); |
} |
/** |
* Provides a list of {@link BeanCreator} for type ofType. |
* |
* @param ofType |
* @param genson |
* @return a list of resolved bean creators |
*/ |
protected abstract List<BeanCreator> provideBeanCreators(Type ofType, Genson genson); |
/** |
* Adds resolved {@link PropertyMutator} to mutatorsMap. |
* |
* @param ofType |
* @param mutatorsMap |
* @param genson |
*/ |
protected abstract void provideBeanPropertyMutators(Type ofType, |
Map<String, LinkedList<PropertyMutator>> mutatorsMap, Genson genson); |
/** |
* Adds resolved {@link PropertyAccessor} to accessorsMap. |
* |
* @param ofType |
* @param accessorsMap |
* @param genson |
*/ |
protected abstract void provideBeanPropertyAccessors(Type ofType, |
Map<String, LinkedList<PropertyAccessor>> accessorsMap, Genson genson); |
/** |
* Implementations of this method can do some additional checks on the creators validity or do |
* any other operations related to creators. This method must merge all creators into a single |
* one. |
* |
* @param creators |
* @return the creator that will be used by the BeanDescriptor |
*/ |
protected abstract BeanCreator checkAndMerge(Type ofType, List<BeanCreator> creators); |
/** |
* Implementations are supposed to merge the {@link PropertyMutator}s from mutators list into a |
* single PropertyMutator. |
* |
* @param name |
* @param mutators |
* @return a single PropertyMutator or null. |
*/ |
protected abstract PropertyMutator checkAndMergeMutators(String name, |
LinkedList<PropertyMutator> mutators); |
/** |
* Implementations may do additional merge operations based on the resolved creator |
* parameters and the resolved mutators. |
* |
* @param ofType |
* @param mutators |
* @param creator |
*/ |
protected abstract void mergeMutatorsWithCreatorProperties(Type ofType, Map<String, PropertyMutator> mutators, BeanCreator creator); |
/** |
* Implementations may do additional merge operations based on the resolved creator |
* parameters and the resolved accessors. |
* |
* @param ofType |
* @param accessors |
* @param creator |
*/ |
protected abstract void mergeAccessorsWithCreatorProperties(Type ofType, List<PropertyAccessor> accessors, BeanCreator creator); |
/** |
* Implementations are supposed to merge the {@link PropertyAccessor}s from accessors list into |
* a single PropertyAccessor. |
* |
* @param name |
* @param accessors |
* @return a single property accessor for this name |
*/ |
protected abstract PropertyAccessor checkAndMergeAccessors(String name, |
LinkedList<PropertyAccessor> accessors); |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/BeanDescriptor.java |
---|
New file |
0,0 → 1,204 |
package com.owlike.genson.reflect; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.Collections; |
import java.util.Comparator; |
import java.util.LinkedHashMap; |
import java.util.List; |
import java.util.Map; |
import com.owlike.genson.*; |
import com.owlike.genson.reflect.BeanCreator.BeanCreatorProperty; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
/** |
* BeanDescriptors are used to serialize/deserialize objects based on their fields, methods and |
* constructors. By default it is supposed to work on JavaBeans, however it can be configured and |
* extended to support different kind of objects. |
* <p/> |
* In most cases BeanDescriptors should not be used directly as it is used internally to support |
* objects not handled by the default Converters. The most frequent case when you will use directly |
* a BeanDescriptor is when you want to deserialize into an existing instance. Here is an example : |
* <p/> |
* <pre> |
* Genson genson = new Genson(); |
* BeanDescriptorProvider provider = genson.getBeanDescriptorProvider(); |
* BeanDescriptor<MyClass> descriptor = provider.provide(MyClass.class, genson); |
* |
* MyClass existingInstance = descriptor.deserialize(existingInstance, new JsonReader("{}"), |
* new Context(genson)); |
* </pre> |
* |
* @param <T> type that this BeanDescriptor can serialize and deserialize. |
* @author eugen |
* @see BeanDescriptorProvider |
*/ |
public class BeanDescriptor<T> implements Converter<T> { |
final Class<?> fromDeclaringClass; |
final Class<T> ofClass; |
final Map<String, PropertyMutator> mutableProperties; |
final List<PropertyAccessor> accessibleProperties; |
final boolean failOnMissingProperty; |
final BeanCreator creator; |
private final boolean _noArgCtr; |
private static final Object MISSING = new Object(); |
// Used as a cache so we just copy it instead of recreating and assigning the default values |
private Object[] globalCreatorArgs; |
private final static Comparator<BeanProperty> _readablePropsComparator = new Comparator<BeanProperty>() { |
public int compare(BeanProperty o1, BeanProperty o2) { |
return o1.name.compareToIgnoreCase(o2.name); |
} |
}; |
public BeanDescriptor(Class<T> forClass, Class<?> fromDeclaringClass, |
List<PropertyAccessor> readableBps, |
Map<String, PropertyMutator> writableBps, BeanCreator creator, |
boolean failOnMissingProperty) { |
this.ofClass = forClass; |
this.fromDeclaringClass = fromDeclaringClass; |
this.creator = creator; |
this.failOnMissingProperty = failOnMissingProperty; |
mutableProperties = writableBps; |
Collections.sort(readableBps, _readablePropsComparator); |
accessibleProperties = Collections.unmodifiableList(readableBps); |
if (this.creator != null) { |
_noArgCtr = this.creator.parameters.size() == 0; |
globalCreatorArgs = new Object[creator.parameters.size()]; |
Arrays.fill(globalCreatorArgs, MISSING); |
} else { |
_noArgCtr = false; |
} |
} |
public boolean isReadable() { |
return !accessibleProperties.isEmpty(); |
} |
public boolean isWritable() { |
return creator != null; |
} |
public void serialize(T obj, ObjectWriter writer, Context ctx) { |
writer.beginObject(); |
RuntimePropertyFilter runtimePropertyFilter = ctx.genson.runtimePropertyFilter(); |
for (PropertyAccessor accessor : accessibleProperties) { |
if (runtimePropertyFilter.shouldInclude(accessor, ctx)) accessor.serialize(obj, writer, ctx); |
} |
writer.endObject(); |
} |
public T deserialize(ObjectReader reader, Context ctx) { |
T bean = null; |
// optimization for default ctr |
if (_noArgCtr) { |
bean = ofClass.cast(creator.create()); |
deserialize(bean, reader, ctx); |
} else { |
if (creator == null) |
throw new JsonBindingException("No constructor has been found for type " |
+ ofClass); |
bean = _deserWithCtrArgs(reader, ctx); |
} |
return bean; |
} |
public void deserialize(T into, ObjectReader reader, Context ctx) { |
reader.beginObject(); |
RuntimePropertyFilter runtimePropertyFilter = ctx.genson.runtimePropertyFilter(); |
for (; reader.hasNext(); ) { |
reader.next(); |
String propName = reader.name(); |
PropertyMutator mutator = mutableProperties.get(propName); |
if (mutator != null) { |
if (runtimePropertyFilter.shouldInclude(mutator, ctx)) { |
mutator.deserialize(into, reader, ctx); |
} else { |
reader.skipValue(); |
} |
} else if (failOnMissingProperty) throw missingPropertyException(propName); |
else reader.skipValue(); |
} |
reader.endObject(); |
} |
protected T _deserWithCtrArgs(ObjectReader reader, Context ctx) { |
List<String> names = new ArrayList<String>(); |
List<Object> values = new ArrayList<Object>(); |
RuntimePropertyFilter runtimePropertyFilter = ctx.genson.runtimePropertyFilter(); |
reader.beginObject(); |
for (; reader.hasNext(); ) { |
reader.next(); |
String propName = reader.name(); |
PropertyMutator muta = mutableProperties.get(propName); |
if (muta != null) { |
if (runtimePropertyFilter.shouldInclude(muta, ctx)) { |
Object param = muta.deserialize(reader, ctx); |
names.add(propName); |
values.add(param); |
} else { |
reader.skipValue(); |
} |
} else if (failOnMissingProperty) throw missingPropertyException(propName); |
else reader.skipValue(); |
} |
int size = names.size(); |
int foundCtrParameters = 0; |
Object[] creatorArgs = globalCreatorArgs.clone(); |
String[] newNames = new String[size]; |
Object[] newValues = new Object[size]; |
for (int i = 0, j = 0; i < size; i++) { |
BeanCreatorProperty mp = creator.paramsAndAliases.get(names.get(i)); |
if (mp != null) { |
creatorArgs[mp.index] = values.get(i); |
foundCtrParameters++; |
} else { |
newNames[j] = names.get(i); |
newValues[j] = values.get(i); |
j++; |
} |
} |
if (foundCtrParameters < creator.parameters.size()) updateWithDefaultValues(creatorArgs, ctx.genson); |
T bean = ofClass.cast(creator.create(creatorArgs)); |
for (int i = 0; i < size; i++) { |
PropertyMutator property = mutableProperties.get(newNames[i]); |
if (property != null) property.mutate(bean, newValues[i]); |
} |
reader.endObject(); |
return bean; |
} |
private void updateWithDefaultValues(Object[] creatorArgs, Genson genson) { |
for (int i = 0; i < creatorArgs.length; i++) { |
if (creatorArgs[i] == MISSING) { |
for (BeanCreatorProperty property : creator.parameters.values()) { |
if (property.index == i) { |
creatorArgs[i] = genson.defaultValue(property.getRawClass()); |
break; |
} |
} |
} |
} |
} |
public Class<T> getOfClass() { |
return ofClass; |
} |
private JsonBindingException missingPropertyException(String name) { |
return new JsonBindingException("No matching property in " + getOfClass() + " for key " + name); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/PropertyAccessor.java |
---|
New file |
0,0 → 1,117 |
package com.owlike.genson.reflect; |
import java.lang.annotation.Annotation; |
import java.lang.reflect.Field; |
import java.lang.reflect.InvocationTargetException; |
import java.lang.reflect.Method; |
import java.lang.reflect.Type; |
import com.owlike.genson.*; |
import com.owlike.genson.stream.JsonWriter; |
import com.owlike.genson.stream.ObjectWriter; |
public abstract class PropertyAccessor extends BeanProperty implements Comparable<PropertyAccessor> { |
Serializer<Object> propertySerializer; |
private final char[] escapedName; |
protected PropertyAccessor(String name, Type type, Class<?> declaringClass, Class<?> concreteClass, |
Annotation[] annotations, int modifiers) { |
super(name, type, declaringClass, concreteClass, annotations, modifiers); |
escapedName = JsonWriter.escapeString(name); |
} |
public void serialize(Object propertySource, ObjectWriter writer, Context ctx) { |
Object propertyValue = access(propertySource); |
writer.writeEscapedName(escapedName); |
try { |
propertySerializer.serialize(propertyValue, writer, ctx); |
} catch (Throwable th) { |
throw couldNotSerialize(th); |
} |
} |
public abstract Object access(final Object target); |
public int compareTo(PropertyAccessor o) { |
return o.priority() - priority(); |
} |
protected JsonBindingException couldNotAccess(Exception e) { |
return new JsonBindingException("Could not access value of property named '" |
+ name + "' using accessor " + signature() + " from class " |
+ declaringClass.getName(), e); |
} |
protected JsonBindingException couldNotSerialize(Throwable e) { |
return new JsonBindingException("Could not serialize property '" + name |
+ "' from class " + declaringClass.getName(), e); |
} |
public static class MethodAccessor extends PropertyAccessor { |
protected final Method _getter; |
public MethodAccessor(String name, Method getter, Type type, Class<?> concreteClass) { |
super(name, type, getter.getDeclaringClass(), concreteClass, getter.getAnnotations(), getter.getModifiers()); |
this._getter = getter; |
if (!_getter.isAccessible()) { |
_getter.setAccessible(true); |
} |
} |
@Override |
public Object access(final Object target) { |
try { |
return _getter.invoke(target); |
} catch (IllegalArgumentException e) { |
throw couldNotAccess(e); |
} catch (IllegalAccessException e) { |
throw couldNotAccess(e); |
} catch (InvocationTargetException e) { |
throw couldNotAccess(e); |
} |
} |
@Override |
String signature() { |
return _getter.toGenericString(); |
} |
@Override |
int priority() { |
return 100; |
} |
} |
public static class FieldAccessor extends PropertyAccessor { |
protected final Field _field; |
public FieldAccessor(String name, Field field, Type type, Class<?> concreteClass) { |
super(name, type, field.getDeclaringClass(), concreteClass, field.getAnnotations(), field.getModifiers()); |
this._field = field; |
if (!_field.isAccessible()) { |
_field.setAccessible(true); |
} |
} |
@Override |
public Object access(final Object target) { |
try { |
return _field.get(target); |
} catch (IllegalArgumentException e) { |
throw couldNotAccess(e); |
} catch (IllegalAccessException e) { |
throw couldNotAccess(e); |
} |
} |
@Override |
public String signature() { |
return _field.toGenericString(); |
} |
@Override |
public int priority() { |
return 50; |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/PropertyNameResolver.java |
---|
New file |
0,0 → 1,195 |
package com.owlike.genson.reflect; |
import java.lang.annotation.Annotation; |
import java.lang.reflect.AnnotatedElement; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.Field; |
import java.lang.reflect.Method; |
import java.util.Arrays; |
import java.util.Iterator; |
import java.util.LinkedList; |
import java.util.List; |
import com.owlike.genson.annotation.JsonProperty; |
/** |
* This interface is intended to be implemented by classes who want to change the way genson does |
* name resolution. The resolved name will be used in the generated stream during serialization and |
* injected into constructors/or setters during deserialization. If you can not resolve the name |
* just return null. You can have a look at the <a href= |
* "http://code.google.com/p/genson/source/browse/src/main/java/com/owlike/genson/reflect/PropertyNameResolver.java" |
* >source code</a> for an example. |
* |
* @author eugen |
* @see com.owlike.genson.annotation.JsonProperty JsonProperty |
*/ |
public interface PropertyNameResolver { |
/** |
* Resolve the parameter name on position parameterIdx in the constructor fromConstructor. |
* |
* @param parameterIdx |
* @param fromConstructor |
* @return the resolved name of the parameter or null |
*/ |
public String resolve(int parameterIdx, Constructor<?> fromConstructor); |
/** |
* Resolve the name of the parameter with parameterIdx as index in fromMethod method. |
* |
* @param parameterIdx |
* @param fromMethod |
* @return the resolved name of the parameter or null |
*/ |
public String resolve(int parameterIdx, Method fromMethod); |
/** |
* Resolve the property name from this field. |
* |
* @param fromField - the field to use for name resolution. |
* @return the resolved name or null. |
*/ |
public String resolve(Field fromField); |
/** |
* Resolve the property name from this method. |
* |
* @param fromMethod - the method to be used for name resolution. |
* @return the resolved name or null. |
*/ |
public String resolve(Method fromMethod); |
public static class CompositePropertyNameResolver implements PropertyNameResolver { |
private List<PropertyNameResolver> components; |
public CompositePropertyNameResolver(List<PropertyNameResolver> components) { |
if (components == null || components.isEmpty()) { |
throw new IllegalArgumentException( |
"The composite resolver must have at least one resolver as component!"); |
} |
this.components = new LinkedList<PropertyNameResolver>(components); |
} |
public CompositePropertyNameResolver add(PropertyNameResolver... resolvers) { |
// should at the head position so custom resolvers a privileged |
components.addAll(0, Arrays.asList(resolvers)); |
return this; |
} |
public String resolve(int parameterIdx, Constructor<?> fromConstructor) { |
String resolvedName = null; |
for (Iterator<PropertyNameResolver> it = components.iterator(); resolvedName == null |
&& it.hasNext(); ) { |
resolvedName = it.next().resolve(parameterIdx, fromConstructor); |
} |
return resolvedName; |
} |
public String resolve(int parameterIdx, Method fromMethod) { |
String resolvedName = null; |
for (Iterator<PropertyNameResolver> it = components.iterator(); resolvedName == null |
&& it.hasNext(); ) { |
resolvedName = it.next().resolve(parameterIdx, fromMethod); |
} |
return resolvedName; |
} |
public String resolve(Field fromField) { |
String resolvedName = null; |
for (Iterator<PropertyNameResolver> it = components.iterator(); resolvedName == null |
&& it.hasNext(); ) { |
resolvedName = it.next().resolve(fromField); |
} |
return resolvedName; |
} |
public String resolve(Method fromMethod) { |
String resolvedName = null; |
for (Iterator<PropertyNameResolver> it = components.iterator(); resolvedName == null |
&& it.hasNext(); ) { |
resolvedName = it.next().resolve(fromMethod); |
} |
return resolvedName; |
} |
} |
public static class ConventionalBeanPropertyNameResolver implements PropertyNameResolver { |
public String resolve(int parameterIdx, Constructor<?> fromConstructor) { |
return null; |
} |
public String resolve(Field fromField) { |
return fromField.getName(); |
} |
public String resolve(Method fromMethod) { |
String name = fromMethod.getName(); |
int length = -1; |
if (name.startsWith("get")) |
length = 3; |
else if (name.startsWith("is")) |
length = 2; |
else if (name.startsWith("set")) |
length = 3; |
if (length > -1 && length < name.length()) { |
return Character.toLowerCase(name.charAt(length)) + name.substring(length + 1); |
} else |
return null; |
} |
public String resolve(int parameterIdx, Method fromMethod) { |
return null; |
} |
} |
/** |
* JsonProperty resolver based on @JsonProperty annotation. Can be used on fields, methods and |
* constructor parameters. |
*/ |
public static class AnnotationPropertyNameResolver implements PropertyNameResolver { |
public AnnotationPropertyNameResolver() { |
} |
public String resolve(int parameterIdx, Constructor<?> fromConstructor) { |
Annotation[] paramAnns = fromConstructor.getParameterAnnotations()[parameterIdx]; |
String name = null; |
for (int j = 0; j < paramAnns.length; j++) { |
if (paramAnns[j] instanceof JsonProperty) { |
name = ((JsonProperty) paramAnns[j]).value(); |
break; |
} |
} |
return "".equals(name) ? null : name; |
} |
public String resolve(int parameterIdx, Method fromMethod) { |
Annotation[] anns = fromMethod.getParameterAnnotations()[parameterIdx]; |
String name = null; |
for (Annotation ann : anns) { |
if (ann instanceof JsonProperty) { |
name = ((JsonProperty) ann).value(); |
break; |
} |
} |
return "".equals(name) ? null : name; |
} |
public String resolve(Field fromField) { |
return getName(fromField); |
} |
public String resolve(Method fromMethod) { |
return getName(fromMethod); |
} |
protected String getName(AnnotatedElement annElement) { |
JsonProperty name = annElement.getAnnotation(JsonProperty.class); |
return name != null && name.value() != null && !name.value().isEmpty() ? name.value() |
: null; |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/BaseBeanDescriptorProvider.java |
---|
New file |
0,0 → 1,346 |
package com.owlike.genson.reflect; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.Field; |
import java.lang.reflect.Method; |
import java.lang.reflect.Modifier; |
import java.lang.reflect.Type; |
import java.util.ArrayDeque; |
import java.util.ArrayList; |
import java.util.Collections; |
import java.util.Comparator; |
import java.util.Iterator; |
import java.util.LinkedList; |
import java.util.List; |
import java.util.Map; |
import com.owlike.genson.Genson; |
import com.owlike.genson.annotation.JsonCreator; |
import com.owlike.genson.reflect.BeanCreator.BeanCreatorProperty; |
import static com.owlike.genson.reflect.TypeUtil.*; |
import static com.owlike.genson.Trilean.*; |
/** |
* Standard implementation of AbstractBeanDescriptorProvider that uses |
* {@link BeanMutatorAccessorResolver} and {@link PropertyNameResolver}. If you want to change the |
* way BeanDescriptors are created you can subclass this class and override the needed methods. If |
* you only want to create instances of your own PropertyMutators/PropertyAccessors or BeanCreators |
* just override the corresponding createXXX methods. |
* |
* @author eugen |
*/ |
public class BaseBeanDescriptorProvider extends AbstractBeanDescriptorProvider { |
private final static Comparator<BeanCreator> _beanCreatorsComparator = new Comparator<BeanCreator>() { |
public int compare(BeanCreator o1, BeanCreator o2) { |
return o1.parameters.size() - o2.parameters.size(); |
} |
}; |
private final BeanPropertyFactory propertyFactory; |
protected final BeanMutatorAccessorResolver mutatorAccessorResolver; |
protected final PropertyNameResolver nameResolver; |
protected final boolean useGettersAndSetters; |
protected final boolean useFields; |
protected final boolean favorEmptyCreators; |
public BaseBeanDescriptorProvider(ContextualConverterFactory ctxConverterFactory, BeanPropertyFactory propertyFactory, |
BeanMutatorAccessorResolver mutatorAccessorResolver, PropertyNameResolver nameResolver, |
boolean useGettersAndSetters, boolean useFields, boolean favorEmptyCreators) { |
super(ctxConverterFactory); |
if (mutatorAccessorResolver == null) |
throw new IllegalArgumentException("mutatorAccessorResolver must be not null!"); |
if (nameResolver == null) |
throw new IllegalArgumentException("nameResolver must be not null!"); |
if (propertyFactory == null) |
throw new IllegalArgumentException("propertyFactory must be not null!"); |
this.propertyFactory = propertyFactory; |
this.mutatorAccessorResolver = mutatorAccessorResolver; |
this.nameResolver = nameResolver; |
this.useFields = useFields; |
this.useGettersAndSetters = useGettersAndSetters; |
if (!useFields && !useGettersAndSetters) |
throw new IllegalArgumentException("You must allow at least one mode: with fields or methods."); |
this.favorEmptyCreators = favorEmptyCreators; |
} |
@Override |
public List<BeanCreator> provideBeanCreators(Type ofType, Genson genson) { |
List<BeanCreator> creators = new ArrayList<BeanCreator>(); |
Class<?> ofClass = getRawClass(ofType); |
if (ofClass.isMemberClass() && (ofClass.getModifiers() & Modifier.STATIC) == 0) |
return creators; |
provideConstructorCreators(ofType, creators, genson); |
for (Class<?> clazz = ofClass; clazz != null && !Object.class.equals(clazz); clazz = clazz |
.getSuperclass()) { |
provideMethodCreators(clazz, creators, ofType, genson); |
} |
return creators; |
} |
@Override |
public void provideBeanPropertyAccessors(Type ofType, |
Map<String, LinkedList<PropertyAccessor>> accessorsMap, Genson genson) { |
ArrayDeque<Class<?>> classesToInspect = new ArrayDeque<Class<?>>(); |
classesToInspect.push(getRawClass(ofType)); |
while (!classesToInspect.isEmpty()) { |
Class<?> clazz = classesToInspect.pop(); |
if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) { |
classesToInspect.push(clazz.getSuperclass()); |
} |
for (Class<?> anInterface : clazz.getInterfaces()) classesToInspect.push(anInterface); |
// first lookup for fields |
if (useFields) provideFieldAccessors(clazz, accessorsMap, ofType, genson); |
// and now search methods (getters) |
if (useGettersAndSetters) provideMethodAccessors(clazz, accessorsMap, ofType, genson); |
} |
} |
@Override |
public void provideBeanPropertyMutators(Type ofType, |
Map<String, LinkedList<PropertyMutator>> mutatorsMap, Genson genson) { |
ArrayDeque<Class<?>> classesToInspect = new ArrayDeque<Class<?>>(); |
classesToInspect.push(getRawClass(ofType)); |
while (!classesToInspect.isEmpty()) { |
Class<?> clazz = classesToInspect.pop(); |
if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) { |
classesToInspect.push(clazz.getSuperclass()); |
} |
for (Class<?> anInterface : clazz.getInterfaces()) classesToInspect.push(anInterface); |
// first lookup for fields |
if (useFields) provideFieldMutators(clazz, mutatorsMap, ofType, genson); |
// and now search methods (getters) |
if (useGettersAndSetters) provideMethodMutators(clazz, mutatorsMap, ofType, genson); |
} |
} |
protected void provideConstructorCreators(Type ofType, List<BeanCreator> creators, Genson genson) { |
Class<?> ofClass = getRawClass(ofType); |
Constructor<?>[] ctrs = ofClass.getDeclaredConstructors(); |
for (Constructor<?> ctr : ctrs) { |
if (TRUE == mutatorAccessorResolver.isCreator(ctr, ofClass)) { |
Type[] parameterTypes = ctr.getGenericParameterTypes(); |
int paramCnt = parameterTypes.length; |
String[] parameterNames = new String[paramCnt]; |
int idx = 0; |
for (; idx < paramCnt; idx++) { |
String name = nameResolver.resolve(idx, ctr); |
if (name == null) break; |
parameterNames[idx] = name; |
} |
if (idx == paramCnt) { |
BeanCreator creator = propertyFactory.createCreator(ofType, ctr, parameterNames, genson); |
creators.add(creator); |
} |
} |
} |
} |
protected void provideMethodCreators(Class<?> ofClass, List<BeanCreator> creators, Type ofType, |
Genson genson) { |
Method[] ctrs = ofClass.getDeclaredMethods(); |
for (Method ctr : ctrs) { |
if (TRUE == mutatorAccessorResolver.isCreator(ctr, getRawClass(ofType))) { |
Type[] parameterTypes = ctr.getGenericParameterTypes(); |
int paramCnt = parameterTypes.length; |
String[] parameterNames = new String[paramCnt]; |
int idx = 0; |
for (; idx < paramCnt; idx++) { |
String name = nameResolver.resolve(idx, ctr); |
if (name == null) break; |
parameterNames[idx] = name; |
} |
if (idx == paramCnt) { |
BeanCreator creator = propertyFactory.createCreator(ofType, ctr, parameterNames, genson); |
creators.add(creator); |
} |
} |
} |
} |
protected void provideFieldAccessors(Class<?> ofClass, |
Map<String, LinkedList<PropertyAccessor>> accessorsMap, Type ofType, Genson genson) { |
Field[] fields = ofClass.getDeclaredFields(); |
for (Field field : fields) { |
if (TRUE == mutatorAccessorResolver.isAccessor(field, getRawClass(ofType))) { |
String name = nameResolver.resolve(field); |
if (name == null) { |
throw new IllegalStateException("Field '" + field.getName() + "' from class " |
+ ofClass.getName() |
+ " has been discovered as accessor but its name couldn't be resolved!"); |
} |
PropertyAccessor accessor = propertyFactory.createAccessor(name, field, ofType, genson); |
update(accessor, accessorsMap); |
} |
} |
} |
protected void provideMethodAccessors(Class<?> ofClass, |
Map<String, LinkedList<PropertyAccessor>> accessorsMap, Type ofType, Genson genson) { |
Method[] methods = ofClass.getDeclaredMethods(); |
for (Method method : methods) { |
if (TRUE == mutatorAccessorResolver.isAccessor(method, getRawClass(ofType))) { |
String name = nameResolver.resolve(method); |
if (name == null) { |
throw new IllegalStateException("Method '" + method.getName() + "' from class " |
+ ofClass.getName() |
+ " has been discovered as accessor but its name couldn't be resolved!"); |
} |
PropertyAccessor accessor = propertyFactory.createAccessor(name, method, ofType, genson); |
update(accessor, accessorsMap); |
} |
} |
} |
protected void provideFieldMutators(Class<?> ofClass, |
Map<String, LinkedList<PropertyMutator>> mutatorsMap, Type ofType, Genson genson) { |
Field[] fields = ofClass.getDeclaredFields(); |
for (Field field : fields) { |
if (TRUE == mutatorAccessorResolver.isMutator(field, getRawClass(ofType))) { |
String name = nameResolver.resolve(field); |
if (name == null) { |
throw new IllegalStateException("Field '" + field.getName() + "' from class " |
+ ofClass.getName() |
+ " has been discovered as mutator but its name couldn't be resolved!"); |
} |
PropertyMutator mutator = propertyFactory.createMutator(name, field, ofType, genson); |
update(mutator, mutatorsMap); |
} |
} |
} |
protected void provideMethodMutators(Class<?> ofClass, |
Map<String, LinkedList<PropertyMutator>> mutatorsMap, Type ofType, Genson genson) { |
Method[] methods = ofClass.getDeclaredMethods(); |
for (Method method : methods) { |
if (TRUE == mutatorAccessorResolver.isMutator(method, getRawClass(ofType))) { |
String name = nameResolver.resolve(method); |
if (name == null) { |
throw new IllegalStateException("Method '" + method.getName() + "' from class " |
+ ofClass.getName() |
+ " has been discovered as mutator but its name couldn't be resolved!"); |
} |
PropertyMutator mutator = propertyFactory.createMutator(name, method, ofType, genson); |
update(mutator, mutatorsMap); |
} |
} |
} |
protected <T extends BeanProperty> void update(T property, Map<String, LinkedList<T>> map) { |
LinkedList<T> accessors = map.get(property.name); |
if (accessors == null) { |
accessors = new LinkedList<T>(); |
map.put(property.name, accessors); |
} |
accessors.add(property); |
} |
@Override |
protected BeanCreator checkAndMerge(Type ofType, List<BeanCreator> creators) { |
Class<?> ofClass = getRawClass(ofType); |
// hum maybe do not check this case as we may have class that will only be serialized so |
// they do not need a ctr? |
// if (creators == null || creators.isEmpty()) |
// throw new IllegalStateException("Could not create BeanDescriptor for type " |
// + ofClass.getName() + ", no creator has been found."); |
if (creators == null || creators.isEmpty()) return null; |
// now lets do the merge |
if (favorEmptyCreators) { |
Collections.sort(creators, _beanCreatorsComparator); |
} |
boolean hasCreatorAnnotation = false; |
BeanCreator creator = null; |
// first lets do some checks |
for (int i = 0; i < creators.size(); i++) { |
BeanCreator ctr = creators.get(i); |
if (ctr.isAnnotationPresent(JsonCreator.class)) { |
if (!hasCreatorAnnotation) |
hasCreatorAnnotation = true; |
else |
_throwCouldCreateBeanDescriptor(ofClass, |
" only one @JsonCreator annotation per class is allowed."); |
} |
} |
if (hasCreatorAnnotation) { |
for (BeanCreator ctr : creators) |
if (ctr.isAnnotationPresent(JsonCreator.class)) return ctr; |
} else { |
creator = creators.get(0); |
} |
return creator; |
} |
protected void _throwCouldCreateBeanDescriptor(Class<?> ofClass, String reason) { |
throw new IllegalStateException("Could not create BeanDescriptor for type " |
+ ofClass.getName() + "," + reason); |
} |
@Override |
protected PropertyAccessor checkAndMergeAccessors(String name, |
LinkedList<PropertyAccessor> accessors) { |
PropertyAccessor accessor = _mostSpecificPropertyDeclaringClass(name, accessors); |
return VisibilityFilter.ABSTRACT.isVisible(accessor.getModifiers()) ? accessor : null; |
} |
@Override |
protected PropertyMutator checkAndMergeMutators(String name, |
LinkedList<PropertyMutator> mutators) { |
PropertyMutator mutator = _mostSpecificPropertyDeclaringClass(name, mutators); |
return VisibilityFilter.ABSTRACT.isVisible(mutator.getModifiers()) ? mutator : null; |
} |
protected <T extends BeanProperty> T _mostSpecificPropertyDeclaringClass(String name, |
LinkedList<T> properties) { |
Iterator<T> it = properties.iterator(); |
T property = it.next(); |
for (; it.hasNext(); ) { |
T next = it.next(); |
// Doesn't matter which one will be used, we want to merge the metadata |
next.updateBoth(property); |
// 1 we search the most specialized class containing this property |
// with highest priority |
if ((property.declaringClass.equals(next.declaringClass) && property.priority() < next.priority()) |
|| property.declaringClass.isAssignableFrom(next.declaringClass)) { |
property = next; |
} else continue; |
} |
return property; |
} |
@Override |
protected void mergeMutatorsWithCreatorProperties(Type ofType, Map<String, PropertyMutator> mutators, BeanCreator creator) { |
for (Map.Entry<String, ? extends BeanCreatorProperty> entry : creator.parameters.entrySet()) { |
PropertyMutator muta = mutators.get(entry.getKey()); |
if (muta == null) { |
// add to mutators only creator properties that don't exist as standard |
// mutator (dont exist as field or method, but only as ctr arg) |
BeanCreatorProperty ctrProperty = entry.getValue(); |
mutators.put(entry.getKey(), ctrProperty); |
} else { |
// update the creator property annotations with mutator annotations and vice versa |
entry.getValue().updateBoth(muta); |
} |
} |
} |
@Override |
protected void mergeAccessorsWithCreatorProperties(Type ofType, List<PropertyAccessor> accessors, BeanCreator creator) { |
// do nothing |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/BeanProperty.java |
---|
New file |
0,0 → 1,110 |
package com.owlike.genson.reflect; |
import java.lang.annotation.Annotation; |
import java.lang.reflect.Type; |
import java.util.Arrays; |
import java.util.Collections; |
import java.util.HashSet; |
import java.util.Set; |
import com.owlike.genson.annotation.JsonProperty; |
/** |
* Represents a bean property, in practice it can be an object field, method (getter/setter) or |
* constructor parameter. |
* |
* @author eugen |
*/ |
public abstract class BeanProperty { |
protected final String name; |
protected final Type type; |
protected final Class<?> declaringClass; |
protected final Class<?> concreteClass; |
protected Annotation[] annotations; |
protected final int modifiers; |
protected BeanProperty(String name, Type type, Class<?> declaringClass, |
Class<?> concreteClass, Annotation[] annotations, int modifiers) { |
this.name = name; |
this.type = type; |
this.declaringClass = declaringClass; |
this.concreteClass = concreteClass; |
this.annotations = annotations; |
this.modifiers = modifiers; |
} |
/** |
* @return The class in which this property is declared |
*/ |
public Class<?> getDeclaringClass() { |
return declaringClass; |
} |
/** |
* @return The final concrete class from which this property has been resolved. |
* For example if this property is defined in class Root but was resolved for class Child extends Root, |
* then getConcreteClass would return Child class and getDeclaringClass would return Root class. |
*/ |
public Class<?> getConcreteClass() { return concreteClass; } |
/** |
* The name of this property (not necessarily the original one). |
*/ |
public String getName() { |
return name; |
} |
/** |
* @return the type of the property |
*/ |
public Type getType() { |
return type; |
} |
public Class<?> getRawClass() { |
return TypeUtil.getRawClass(type); |
} |
public int getModifiers() { |
return modifiers; |
} |
public String[] aliases() { |
JsonProperty ann = getAnnotation(JsonProperty.class); |
return ann != null ? ann.aliases() : new String[]{}; |
} |
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { |
for (Annotation ann : annotations) |
if (annotationClass.isInstance(ann)) return annotationClass.cast(ann); |
return null; |
} |
void updateBoth(BeanProperty otherBeanProperty) { |
// FIXME: we don't care for duplicate annotations as it should not change the behaviour - actually we do as it can change the behaviour... |
// an easy solution would be to forbid duplicate annotations, which can make sense. |
if (annotations.length > 0 || otherBeanProperty.annotations.length > 0) { |
Annotation[] mergedAnnotations = new Annotation[annotations.length + otherBeanProperty.annotations.length]; |
System.arraycopy(annotations, 0, mergedAnnotations, 0, annotations.length); |
System.arraycopy(otherBeanProperty.annotations, 0, mergedAnnotations, annotations.length, otherBeanProperty.annotations.length); |
if (otherBeanProperty.annotations.length > 0) this.annotations = mergedAnnotations; |
// update also the other bean property with the merged result. |
// This is easier rather than do it in one direction and then in the other one. |
if (annotations.length > 0) otherBeanProperty.annotations = mergedAnnotations; |
} |
} |
/** |
* Used to give priority to implementations, for example by default a method would have a higher |
* priority than a field because it can do some logic. The greater the priority value is the |
* more important is this BeanProperty. |
* |
* @return the priority of this BeanProperty |
*/ |
abstract int priority(); |
abstract String signature(); |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/BeanViewDescriptorProvider.java |
---|
New file |
0,0 → 1,269 |
package com.owlike.genson.reflect; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.Field; |
import java.lang.reflect.InvocationTargetException; |
import java.lang.reflect.Method; |
import java.lang.reflect.Modifier; |
import java.lang.reflect.Type; |
import java.util.ArrayList; |
import java.util.List; |
import java.util.Map; |
import java.util.concurrent.ConcurrentHashMap; |
import static com.owlike.genson.Trilean.FALSE; |
import static com.owlike.genson.Trilean.TRUE; |
import static com.owlike.genson.reflect.TypeUtil.*; |
import com.owlike.genson.*; |
import com.owlike.genson.annotation.JsonCreator; |
import com.owlike.genson.reflect.PropertyAccessor.MethodAccessor; |
import com.owlike.genson.reflect.PropertyMutator.MethodMutator; |
/** |
* This class constructs BeanDescriptors for the {@link com.owlike.genson.BeanView BeanView} |
* mechanism. This class is mainly intended for internal use. It can be directly used if needed to |
* get a BeanDescriptor instance for a BeanView (for example if you want to deserialize into an |
* existing object and apply a BeanView). Extending BeanViewDescriptorProvider should be avoided. |
* |
* @author eugen |
*/ |
public class BeanViewDescriptorProvider extends BaseBeanDescriptorProvider { |
private Map<Class<?>, BeanView<?>> views; |
private Map<Class<?>, BeanDescriptor<?>> descriptors = new ConcurrentHashMap<Class<?>, BeanDescriptor<?>>(); |
public BeanViewDescriptorProvider(ContextualConverterFactory ctxConverterFactory, |
Map<Class<?>, BeanView<?>> views, BeanPropertyFactory propertyFactory, |
BeanMutatorAccessorResolver mutatorAccessorResolver, |
PropertyNameResolver nameResolver) { |
super(ctxConverterFactory, propertyFactory, mutatorAccessorResolver, nameResolver, true, false, true); |
this.views = views; |
} |
@SuppressWarnings("unchecked") |
@Override |
public <T> BeanDescriptor<T> provide(Class<T> ofClass, |
Type ofType, Genson genson) { |
Class<?> rawClass = getRawClass(ofType); |
if (!BeanView.class.isAssignableFrom(rawClass)) |
throw new IllegalArgumentException("Expected argument of type " |
+ BeanView.class.getName() + " but provided " + rawClass); |
BeanDescriptor<T> descriptor = (BeanDescriptor<T>) descriptors.get(rawClass); |
if (descriptor == null) { |
Class<?> parameterizedTypeForBeanView = |
getRawClass(expandType(BeanView.class.getTypeParameters()[0], ofType)); |
if (!ofClass.isAssignableFrom(parameterizedTypeForBeanView)) { |
throw new IllegalArgumentException( |
"Expected type for ofClass parameter is " + parameterizedTypeForBeanView |
+ " but provided is " + ofClass); |
} |
try { |
if (!views.containsKey(rawClass)) { |
Constructor<BeanView<T>> ctr = |
(Constructor<BeanView<T>>) rawClass.getDeclaredConstructor(); |
if (!ctr.isAccessible()) ctr.setAccessible(true); |
views.put(rawClass, ctr.newInstance()); |
} |
descriptor = super.provide(ofClass, ofType, genson); |
descriptors.put(rawClass, descriptor); |
} catch (SecurityException e) { |
throw couldNotInstantiateBeanView(ofClass, e); |
} catch (NoSuchMethodException e) { |
throw couldNotInstantiateBeanView(ofClass, e); |
} catch (IllegalArgumentException e) { |
throw couldNotInstantiateBeanView(ofClass, e); |
} catch (InstantiationException e) { |
throw couldNotInstantiateBeanView(ofClass, e); |
} catch (IllegalAccessException e) { |
throw couldNotInstantiateBeanView(ofClass, e); |
} catch (InvocationTargetException e) { |
throw couldNotInstantiateBeanView(ofClass, e); |
} |
} |
return descriptor; |
} |
private JsonBindingException couldNotInstantiateBeanView(Class<?> beanViewClass, |
Exception e) { |
return new JsonBindingException("Could not instantiate BeanView " |
+ beanViewClass.getName() |
+ ", BeanView implementations must have a public no arg constructor.", e); |
} |
@Override |
public List<BeanCreator> provideBeanCreators(Type ofType, Genson genson) { |
List<BeanCreator> creators = new ArrayList<BeanCreator>(); |
for (Class<?> clazz = getRawClass(ofType); clazz != null && !Object.class.equals(clazz); clazz = |
clazz.getSuperclass()) { |
provideMethodCreators(clazz, creators, ofType, genson); |
} |
Type viewForType = TypeUtil.expandType(BeanView.class.getTypeParameters()[0], ofType); |
List<BeanCreator> oCtrs = super.provideBeanCreators(viewForType, genson); |
creators.addAll(oCtrs); |
return creators; |
} |
public static class BeanViewPropertyFactory implements BeanPropertyFactory { |
private final Map<Class<?>, BeanView<?>> views; |
public BeanViewPropertyFactory(Map<Class<?>, BeanView<?>> views) { |
this.views = views; |
} |
public PropertyAccessor createAccessor(String name, Method method, Type ofType, |
Genson genson) { |
// the target bean must be first (and single) parameter for beanview accessors |
BeanView<?> beanview = views.get(getRawClass(ofType)); |
if (beanview != null) { |
Type superTypeWithParameter = |
TypeUtil.lookupGenericType(BeanView.class, beanview.getClass()); |
Class<?> tClass = |
getRawClass(typeOf(0, |
expandType(superTypeWithParameter, beanview.getClass()))); |
Type type = expandType(method.getGenericReturnType(), ofType); |
return new BeanViewPropertyAccessor(name, method, type, beanview, tClass); |
} else return null; |
} |
public PropertyMutator createMutator(String name, Method method, Type ofType, Genson genson) { |
// the target bean must be second parameter for beanview mutators |
BeanView<?> beanview = views.get(getRawClass(ofType)); |
if (beanview != null) { |
Type superTypeWithParameter = |
TypeUtil.lookupGenericType(BeanView.class, beanview.getClass()); |
Class<?> tClass = |
getRawClass(typeOf(0, |
expandType(superTypeWithParameter, beanview.getClass()))); |
Type type = expandType(method.getGenericParameterTypes()[0], ofType); |
return new BeanViewPropertyMutator(name, method, type, beanview, tClass); |
} else return null; |
} |
@Override |
public PropertyAccessor createAccessor(String name, Field field, Type ofType, |
Genson genson) { |
return null; |
} |
@Override |
public BeanCreator createCreator(Type ofType, Constructor<?> ctr, |
String[] resolvedNames, Genson genson) { |
return null; |
} |
@Override |
public BeanCreator createCreator(Type ofType, Method method, |
String[] resolvedNames, Genson genson) { |
return null; |
} |
@Override |
public PropertyMutator createMutator(String name, Field field, Type ofType, |
Genson genson) { |
return null; |
} |
} |
public static class BeanViewMutatorAccessorResolver implements BeanMutatorAccessorResolver { |
public Trilean isAccessor(Field field, Class<?> baseClass) { |
return FALSE; |
} |
public Trilean isAccessor(Method method, Class<?> baseClass) { |
Type expectedType = TypeUtil.lookupGenericType(BeanView.class, baseClass); |
expectedType = TypeUtil.expandType(expectedType, baseClass); |
expectedType = TypeUtil.typeOf(0, expectedType); |
int modifiers = method.getModifiers(); |
return Trilean.valueOf((method.getName().startsWith("get") || (method.getName() |
.startsWith("is") && (TypeUtil.match(method.getGenericReturnType(), |
Boolean.class, false) || boolean.class.equals(method.getReturnType())))) |
&& TypeUtil.match(expectedType, method.getGenericParameterTypes()[0], false) |
&& Modifier.isPublic(modifiers) |
&& !Modifier.isAbstract(modifiers) |
&& !Modifier.isNative(modifiers)); |
} |
public Trilean isCreator(Constructor<?> constructor, Class<?> baseClass) { |
int modifier = constructor.getModifiers(); |
return Trilean.valueOf(Modifier.isPublic(modifier) |
|| !(Modifier.isPrivate(modifier) || Modifier.isProtected(modifier))); |
} |
public Trilean isCreator(Method method, Class<?> baseClass) { |
if (method.getAnnotation(JsonCreator.class) != null) { |
if (Modifier.isStatic(method.getModifiers())) return TRUE; |
throw new JsonBindingException("Method " + method.toGenericString() |
+ " annotated with @Creator must be static!"); |
} |
return FALSE; |
} |
public Trilean isMutator(Field field, Class<?> baseClass) { |
return FALSE; |
} |
public Trilean isMutator(Method method, Class<?> baseClass) { |
Type expectedType = TypeUtil.lookupGenericType(BeanView.class, baseClass); |
expectedType = TypeUtil.expandType(expectedType, baseClass); |
expectedType = TypeUtil.typeOf(0, expectedType); |
int modifiers = method.getModifiers(); |
return Trilean.valueOf(method.getName().startsWith("set") |
&& void.class.equals(method.getReturnType()) |
&& method.getGenericParameterTypes().length == 2 |
&& TypeUtil.match(expectedType, method.getGenericParameterTypes()[1], false) |
&& Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers) |
&& !Modifier.isNative(modifiers)); |
} |
} |
private static class BeanViewPropertyAccessor extends MethodAccessor { |
private final BeanView<?> _view; |
public BeanViewPropertyAccessor(String name, Method getter, Type type, BeanView<?> target, |
Class<?> tClass) { |
super(name, getter, type, tClass); |
this._view = target; |
} |
@Override |
public Object access(Object target) { |
try { |
return _getter.invoke(_view, target); |
} catch (IllegalArgumentException e) { |
throw couldNotAccess(e); |
} catch (IllegalAccessException e) { |
throw couldNotAccess(e); |
} catch (InvocationTargetException e) { |
throw couldNotAccess(e); |
} |
} |
} |
private static class BeanViewPropertyMutator extends MethodMutator { |
private final BeanView<?> _view; |
public BeanViewPropertyMutator(String name, Method setter, Type type, BeanView<?> target, |
Class<?> tClass) { |
super(name, setter, type, tClass); |
this._view = target; |
} |
@Override |
public void mutate(Object target, Object value) { |
try { |
_setter.invoke(_view, value, target); |
} catch (IllegalArgumentException e) { |
throw couldNotMutate(e); |
} catch (IllegalAccessException e) { |
throw couldNotMutate(e); |
} catch (InvocationTargetException e) { |
throw couldNotMutate(e); |
} |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/BeanDescriptorProvider.java |
---|
New file |
0,0 → 1,70 |
package com.owlike.genson.reflect; |
import java.lang.reflect.Type; |
import java.util.ArrayList; |
import java.util.List; |
import java.util.concurrent.ConcurrentHashMap; |
import com.owlike.genson.Genson; |
/** |
* Interface implemented by classes who want to provide {@link BeanDescriptor} instances for the |
* specified type. |
* |
* @author eugen |
*/ |
public interface BeanDescriptorProvider { |
/** |
* Provides a BeanDescriptor for "type" using current Genson instance. |
* |
* @param type for which we need a BeanDescriptor. |
* @param genson current instance. |
* @return A BeanDescriptor instance able to serialize/deserialize objects of type T. |
*/ |
public <T> BeanDescriptor<T> provide(Class<T> type, Genson genson); |
/** |
* Provides a BeanDescriptor that can serialize/deserialize "ofClass" type, based on "type" |
* argument. The arguments "ofClass" and "type" will be the same in most cases, but for example |
* in BeanViews ofClass will correspond to the parameterized type and "type" to the BeanView |
* implementation. |
* |
* @param ofClass is the Class for which we need a BeanDescriptor that will be able to |
* serialize/deserialize objects of that type; |
* @param type to use to build this descriptor (use its declared methods, fields, etc). |
* @param genson is the current Genson instance. |
* @return A BeanDescriptor instance able to serialize/deserialize objects of type ofClass. |
*/ |
public <T> BeanDescriptor<T> provide(Class<T> ofClass, Type type, Genson genson); |
public static class CompositeBeanDescriptorProvider implements BeanDescriptorProvider { |
private final List<BeanDescriptorProvider> providers; |
private final ConcurrentHashMap<Type, BeanDescriptor<?>> cache = new ConcurrentHashMap<Type, BeanDescriptor<?>>(); |
public CompositeBeanDescriptorProvider(List<BeanDescriptorProvider> providers) { |
this.providers = new ArrayList<BeanDescriptorProvider>(providers); |
} |
@Override |
public <T> BeanDescriptor<T> provide(Class<T> ofClass, Genson genson) { |
return provide(ofClass, ofClass, genson); |
} |
@Override |
public <T> BeanDescriptor<T> provide(Class<T> ofClass, Type type, Genson genson) { |
BeanDescriptor<T> desc = (BeanDescriptor<T>) cache.get(type); |
if (desc == null) { |
for (BeanDescriptorProvider provider : providers) { |
desc = provider.provide(ofClass, type, genson); |
if (desc != null) break; |
} |
cache.putIfAbsent(type, desc); |
} |
return desc; |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/TypeUtil.java |
---|
New file |
0,0 → 1,600 |
package com.owlike.genson.reflect; |
import java.lang.reflect.Array; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.GenericArrayType; |
import java.lang.reflect.GenericDeclaration; |
import java.lang.reflect.Method; |
import java.lang.reflect.ParameterizedType; |
import java.lang.reflect.Type; |
import java.lang.reflect.TypeVariable; |
import java.lang.reflect.WildcardType; |
import java.util.Arrays; |
import java.util.Collection; |
import java.util.HashMap; |
import java.util.Map; |
import java.util.concurrent.ConcurrentHashMap; |
import com.owlike.genson.Operations; |
/** |
* This class provides utilities to work with java Types. Its main goal is to provide tools for working with generic |
* types. |
* |
* @author eugen |
*/ |
/* |
* TODO ExpandedType do we need a reference to an original type?=> Not nice to provide 2 |
* implementations for ParameterizedType... |
*/ |
public final class TypeUtil { |
private final static Map<Class<?>, Class<?>> _wrappedPrimitives = new HashMap<Class<?>, Class<?>>(); |
static { |
_wrappedPrimitives.put(int.class, Integer.class); |
_wrappedPrimitives.put(double.class, Double.class); |
_wrappedPrimitives.put(long.class, Long.class); |
_wrappedPrimitives.put(float.class, Float.class); |
_wrappedPrimitives.put(short.class, Short.class); |
_wrappedPrimitives.put(boolean.class, Boolean.class); |
_wrappedPrimitives.put(char.class, Character.class); |
_wrappedPrimitives.put(byte.class, Byte.class); |
_wrappedPrimitives.put(void.class, Void.class); |
} |
private final static Map<TypeAndRootClassKey, Type> _cache = new ConcurrentHashMap<TypeUtil.TypeAndRootClassKey, Type>( |
32); |
public final static Class<?> wrap(Class<?> clazz) { |
Class<?> wrappedClass = _wrappedPrimitives.get(clazz); |
return wrappedClass == null ? clazz : wrappedClass; |
} |
private final static ThreadLocal<Map<Type, Type>> _circularExpandedType = new ThreadLocal<Map<Type, Type>>(); |
/** |
* Expands type in the type rootType to Class, ParameterizedType or GenericArrayType. Useful for generic types. |
* rootType is used to get the specialization information for expansion. |
*/ |
public final static Type expandType(final Type type, final Type rootType) { |
/* TODO in case where it is a class we should maybe still try to expand it using rootType information? |
* for example if I want to expand Map in rootType context Map<String, Integer>, actually this does not work. |
* However such modification should be done with more care. Impacts on typeOf and lookUpGenericType, probably others too. |
*/ |
if (type instanceof ExpandedType || type instanceof Class) |
return type; |
Map<Type, Type> circularTypes = _circularExpandedType.get(); |
if (circularTypes == null) { |
circularTypes = new HashMap<Type, Type>(); |
_circularExpandedType.set(circularTypes); |
} |
// this allows to handle cyclic generic types (types that refer to them self) |
if (circularTypes.containsKey(type)) { |
return circularTypes.get(type); |
} else { |
try { |
circularTypes.put(type, getRawClass(type)); |
TypeAndRootClassKey key = new TypeAndRootClassKey(type, rootType); |
Type expandedType = _cache.get(key); |
if (expandedType == null) { |
if (type instanceof ParameterizedType) { |
ParameterizedType pType = (ParameterizedType) type; |
Type[] args = pType.getActualTypeArguments(); |
int len = args.length; |
Type[] expandedArgs = new Type[len]; |
for (int i = 0; i < len; i++) { |
expandedArgs[i] = expandType(args[i], rootType); |
} |
expandedType = new ExpandedParameterizedType(pType, getRawClass(rootType), expandedArgs); |
} else if (type instanceof TypeVariable) { |
@SuppressWarnings("unchecked") |
TypeVariable<GenericDeclaration> tvType = (TypeVariable<GenericDeclaration>) type; |
if (rootType instanceof ParameterizedType) { |
ParameterizedType rootPType = (ParameterizedType) rootType; |
Type[] typeArgs = rootPType.getActualTypeArguments(); |
String typeName = tvType.getName(); |
int idx = 0; |
for (TypeVariable<?> parameter : genericDeclarationToClass(tvType.getGenericDeclaration()) |
.getTypeParameters()) { |
if (typeName.equals(parameter.getName())) { |
expandedType = typeArgs[idx]; |
break; |
} |
idx++; |
} |
} else |
expandedType = resolveTypeVariable(tvType, getRawClass(rootType)); |
if (type == expandedType) |
expandedType = expandType(tvType.getBounds()[0], rootType); |
} else if (type instanceof GenericArrayType) { |
GenericArrayType genArrType = (GenericArrayType) type; |
Type cType = expandType(genArrType.getGenericComponentType(), rootType); |
if (genArrType.getGenericComponentType() == cType) |
cType = Object.class; |
expandedType = new ExpandedGenericArrayType(genArrType, cType, getRawClass(rootType)); |
} else if (type instanceof WildcardType) { |
WildcardType wType = (WildcardType) type; |
// let's expand wildcards to their upper bound or object if no upper bound |
// defined |
// it will simplify things to accept only one upper bound and ignore lower |
// bounds |
// as it is equivalent to Object. |
expandedType = wType.getUpperBounds().length > 0 ? expandType(wType.getUpperBounds()[0], |
rootType) : Object.class; |
} |
if (expandedType == null) |
throw new IllegalArgumentException("Type " + type + " not supported for expansion!"); |
_cache.put(key, expandedType); |
} |
return expandedType; |
} finally { |
circularTypes.remove(type); |
} |
} |
} |
/** |
* Searches for ofClass in the inherited classes and interfaces of inClass. If ofClass has been found in the super |
* classes/interfaces of inClass, then the generic type corresponding to inClass and its TypeVariables is returned, |
* otherwise null. For example : |
* <p/> |
* <pre> |
* abstract class MyClass implements Serializer<Number> { |
* |
* } |
* |
* // type value will be the parameterized type Serializer<Number> |
* Type type = lookupGenericType(Serializer.class, MyClass.class); |
* </pre> |
*/ |
public final static Type lookupGenericType(Class<?> ofClass, Class<?> inClass) { |
if (ofClass == null || inClass == null || !ofClass.isAssignableFrom(inClass)) |
return null; |
if (ofClass.equals(inClass)) |
return inClass; |
if (ofClass.isInterface()) { |
// lets look if the interface is directly implemented by fromClass |
Class<?>[] interfaces = inClass.getInterfaces(); |
for (int i = 0; i < interfaces.length; i++) { |
// do they match? |
if (ofClass.equals(interfaces[i])) { |
return inClass.getGenericInterfaces()[i]; |
} else { |
Type superType = lookupGenericType(ofClass, interfaces[i]); |
if (superType != null) |
return superType; |
} |
} |
} |
// ok it's not one of the directly implemented interfaces, lets try extended class |
Class<?> superClass = inClass.getSuperclass(); |
if (ofClass.equals(superClass)) |
return inClass.getGenericSuperclass(); |
return lookupGenericType(ofClass, inClass.getSuperclass()); |
} |
public final static Class<?> getRawClass(Type type) { |
if (type instanceof Class<?>) |
return (Class<?>) type; |
else if (type instanceof ParameterizedType) { |
ParameterizedType pType = (ParameterizedType) type; |
return (Class<?>) pType.getRawType(); |
} else if (type instanceof GenericArrayType) { |
Type componentType = ((GenericArrayType) type).getGenericComponentType(); |
return Array.newInstance(getRawClass(componentType), 0).getClass(); |
} else |
return getRawClass(expand(type, null)); |
} |
/** |
* Returns the type of this Collection or Array. |
* |
* @throws IllegalArgumentException if type is not a Collection, not a generic array and not a primitive array. |
*/ |
public final static Type getCollectionType(Type type) { |
if (type instanceof GenericArrayType) { |
return ((GenericArrayType) type).getGenericComponentType(); |
} else if (type instanceof Class<?>) { |
Class<?> clazz = (Class<?>) type; |
if (clazz.isArray()) |
return clazz.getComponentType(); |
else if (Collection.class.isAssignableFrom(clazz)) { |
return Object.class; |
} |
} else if (type instanceof ParameterizedType && Collection.class.isAssignableFrom(getRawClass(type))) { |
return typeOf(0, type); |
} |
throw new IllegalArgumentException( |
"Could not extract parametrized type, are you sure it is a Collection or an Array?"); |
} |
// protected for testing. |
final static Type expand(Type type, Class<?> inClass) { |
Type expandedType = null; |
if (type instanceof TypeVariable) { |
@SuppressWarnings("unchecked") |
// for the moment we assume it is a class, we can later handle ctr and methods |
TypeVariable<GenericDeclaration> tvType = (TypeVariable<GenericDeclaration>) type; |
if (inClass == null) |
inClass = genericDeclarationToClass(tvType.getGenericDeclaration()); |
expandedType = resolveTypeVariable(tvType, inClass); |
if (type.equals(expandedType)) |
expandedType = tvType.getBounds()[0]; |
} else if (type instanceof WildcardType) { |
WildcardType wType = (WildcardType) type; |
expandedType = wType.getUpperBounds().length > 0 ? expand(wType.getUpperBounds()[0], inClass) |
: Object.class; |
} else |
return type; |
return expandedType == null || type.equals(expandedType) ? Object.class : expandedType; |
} |
/** |
* Searches for the typevariable definition in the inClass hierarchy. |
* |
* @param type |
* @param inClass |
* @return the resolved type or type if unable to resolve it. |
*/ |
public final static Type resolveTypeVariable(TypeVariable<? extends GenericDeclaration> type, Class<?> inClass) { |
return resolveTypeVariable(type, genericDeclarationToClass(type.getGenericDeclaration()), inClass); |
} |
private final static Type resolveTypeVariable(TypeVariable<? extends GenericDeclaration> type, |
Class<?> declaringClass, Class<?> inClass) { |
if (inClass == null) |
return null; |
Class<?> superClass = null; |
Type resolvedType = null; |
Type genericSuperClass = null; |
if (!declaringClass.equals(inClass)) { |
if (declaringClass.isInterface()) { |
// the declaringClass is an interface |
Class<?>[] interfaces = inClass.getInterfaces(); |
for (int i = 0; i < interfaces.length && resolvedType == null; i++) { |
superClass = interfaces[i]; |
resolvedType = resolveTypeVariable(type, declaringClass, superClass); |
genericSuperClass = inClass.getGenericInterfaces()[i]; |
} |
} |
if (resolvedType == null) { |
superClass = inClass.getSuperclass(); |
resolvedType = resolveTypeVariable(type, declaringClass, superClass); |
genericSuperClass = inClass.getGenericSuperclass(); |
} |
} else { |
resolvedType = type; |
genericSuperClass = superClass = inClass; |
} |
if (resolvedType != null) { |
// if its another type this means we have finished |
if (resolvedType instanceof TypeVariable<?>) { |
type = (TypeVariable<?>) resolvedType; |
TypeVariable<?>[] parameters = superClass.getTypeParameters(); |
int positionInClass = 0; |
for (; positionInClass < parameters.length && !type.equals(parameters[positionInClass]); positionInClass++) { |
} |
// we located the position of the typevariable in the superclass |
if (positionInClass < parameters.length) { |
// let's look if we have type specialization information in the current class |
if (genericSuperClass instanceof ParameterizedType) { |
ParameterizedType pGenericType = (ParameterizedType) genericSuperClass; |
Type[] args = pGenericType.getActualTypeArguments(); |
return positionInClass < args.length ? args[positionInClass] : null; |
} |
} |
// we didnt find typevariable specialization in the class, so it's the best we can |
// do, lets return the resolvedType... |
} |
} |
return resolvedType; |
} |
/** |
* Deep comparison between type and oType. If parameter strictMatch is true, then type and oType will be strictly |
* compared otherwise this method checks whether oType is assignable from type. |
*/ |
public final static boolean match(Type type, Type oType, boolean strictMatch) { |
if (type == null || oType == null) |
return type == null && oType == null; |
Class<?> clazz = getRawClass(type); |
Class<?> oClazz = getRawClass(oType); |
boolean match = strictMatch ? oClazz.equals(clazz) : oClazz.isAssignableFrom(clazz); |
if (Object.class.equals(oClazz) && !strictMatch) |
return match; |
if (clazz.isArray() && !oClazz.isArray()) |
return match; |
Type[] types = getTypes(type); |
Type[] oTypes = getTypes(oType); |
match = match && (types.length == oTypes.length || types.length == 0); |
for (int i = 0; i < types.length && match; i++) |
match = match(types[i], oTypes[i], strictMatch); |
return match; |
} |
/** |
* Convenient method that returns the type of the parameter at position parameterIdx in the type fromType. |
* |
* @throws UnsupportedOperationException thrown if fromType is not a Class nor a ParameterizedType. |
*/ |
public final static Type typeOf(int parameterIdx, Type fromType) { |
if (fromType instanceof Class<?>) { |
Class<?> tClass = (Class<?>) fromType; |
TypeVariable<?>[] tvs = tClass.getTypeParameters(); |
if (tvs.length > parameterIdx) |
return expandType(tvs[parameterIdx], fromType); |
} else if (fromType instanceof ParameterizedType) { |
ParameterizedType pType = (ParameterizedType) fromType; |
Type[] ts = pType.getActualTypeArguments(); |
if (ts.length > parameterIdx) |
return ts[parameterIdx]; |
} |
throw new UnsupportedOperationException("Couldn't find parameter at " + parameterIdx + " from type " + fromType |
+ " , you should first locate the parameterized type, expand it and then use typeOf."); |
} |
private static Class<?> genericDeclarationToClass(GenericDeclaration declaration) { |
if (declaration instanceof Class) |
return (Class<?>) declaration; |
if (declaration instanceof Method) |
return ((Method) declaration).getDeclaringClass(); |
if (declaration instanceof Constructor) |
return ((Constructor<?>) declaration).getDeclaringClass(); |
throw new UnsupportedOperationException(); |
} |
private final static Type[] getTypes(Type type) { |
if (type instanceof Class) { |
Class<?> tClass = (Class<?>) type; |
if (tClass.isArray()) |
return new Type[]{tClass.getComponentType()}; |
else { |
TypeVariable<?>[] tvs = ((Class<?>) type).getTypeParameters(); |
Type[] types = new Type[tvs.length]; |
int i = 0; |
for (TypeVariable<?> tv : tvs) { |
types[i++] = tv.getBounds()[0]; |
} |
return types; |
} |
} else if (type instanceof ParameterizedType) { |
return ((ParameterizedType) type).getActualTypeArguments(); |
} else if (type instanceof GenericArrayType) { |
return new Type[]{((GenericArrayType) type).getGenericComponentType()}; |
} else if (type instanceof WildcardType) { |
return Operations.union(Type[].class, ((WildcardType) type).getUpperBounds(), |
((WildcardType) type).getLowerBounds()); |
} else if (type instanceof TypeVariable<?>) { |
@SuppressWarnings("unchecked") |
TypeVariable<Class<?>> tvType = (TypeVariable<Class<?>>) type; |
Type resolvedType = resolveTypeVariable(tvType, tvType.getGenericDeclaration()); |
return tvType.equals(resolvedType) ? tvType.getBounds() : new Type[]{resolvedType}; |
} else |
return new Type[0]; |
} |
/* |
* ExpandedType must implement hashcode and equals using only the original type |
* The root class is not significant here as a type expanded in different root classes can yield to the same expanded type. |
* Very important, hashcode and equals must also be implemented in subclasses and also use those from ExpandedType. |
* |
* http://code.google.com/p/genson/issues/detail?id=4 |
*/ |
private static abstract class ExpandedType<T extends Type> { |
protected final T originalType; |
protected final Class<?> rootClass; |
private final int _hash; |
private ExpandedType(T originalType, Class<?> rootClass) { |
if (originalType == null || rootClass == null) |
throw new IllegalArgumentException("Null arg not allowed!"); |
this.originalType = originalType; |
this.rootClass = rootClass; |
final int prime = 31; |
int result = 1; |
_hash = prime * result + ((originalType == null) ? 0 : originalType.hashCode()); |
} |
@SuppressWarnings("unused") |
public T getOriginalType() { |
return originalType; |
} |
@SuppressWarnings("unused") |
public Class<?> getRootClass() { |
return rootClass; |
} |
@Override |
public int hashCode() { |
return _hash; |
} |
@Override |
public boolean equals(Object obj) { |
if (this == obj) |
return true; |
if (obj == null) |
return false; |
if (getClass() != obj.getClass()) |
return false; |
@SuppressWarnings("rawtypes") |
ExpandedType other = (ExpandedType) obj; |
if (originalType == null) { |
if (other.originalType != null) |
return false; |
} else if (!originalType.equals(other.originalType)) |
return false; |
return true; |
} |
} |
/* |
* http://code.google.com/p/genson/issues/detail?id=4 |
*/ |
private final static class ExpandedGenericArrayType extends ExpandedType<GenericArrayType> implements |
GenericArrayType { |
private final Type componentType; |
private final int _hash; |
public ExpandedGenericArrayType(GenericArrayType originalType, Type componentType, Class<?> rootClass) { |
super(originalType, rootClass); |
if (componentType == null) |
throw new IllegalArgumentException("Null arg not allowed!"); |
this.componentType = componentType; |
final int prime = 31; |
int result = super.hashCode(); |
_hash = prime * result + ((componentType == null) ? 0 : componentType.hashCode()); |
} |
public Type getGenericComponentType() { |
return componentType; |
} |
@Override |
public int hashCode() { |
return _hash; |
} |
@Override |
public boolean equals(Object obj) { |
if (this == obj) |
return true; |
if (!super.equals(obj)) |
return false; |
if (getClass() != obj.getClass()) |
return false; |
ExpandedGenericArrayType other = (ExpandedGenericArrayType) obj; |
if (componentType == null) { |
if (other.componentType != null) |
return false; |
} else if (!componentType.equals(other.componentType)) |
return false; |
return true; |
} |
} |
/* |
* http://code.google.com/p/genson/issues/detail?id=4 |
*/ |
private final static class ExpandedParameterizedType extends ExpandedType<ParameterizedType> implements |
ParameterizedType { |
private final Type[] typeArgs; |
private final int _hash; |
public ExpandedParameterizedType(ParameterizedType originalType, Class<?> rootClass, Type[] typeArgs) { |
super(originalType, rootClass); |
if (typeArgs == null) |
throw new IllegalArgumentException("Null arg not allowed!"); |
this.typeArgs = typeArgs; |
final int prime = 31; |
int result = super.hashCode(); |
_hash = prime * result + Arrays.hashCode(typeArgs); |
} |
public Type[] getActualTypeArguments() { |
return typeArgs; |
} |
public Type getOwnerType() { |
return originalType.getOwnerType(); |
} |
public Type getRawType() { |
return originalType.getRawType(); |
} |
@Override |
public int hashCode() { |
return _hash; |
} |
@Override |
public boolean equals(Object obj) { |
if (this == obj) |
return true; |
if (!super.equals(obj)) |
return false; |
if (getClass() != obj.getClass()) |
return false; |
ExpandedParameterizedType other = (ExpandedParameterizedType) obj; |
if (!Arrays.equals(typeArgs, other.typeArgs)) |
return false; |
return true; |
} |
} |
/* |
* No changes here, but it is important to keep using the rootType for computing the hashcode and equals |
* as this is used to check if a type needs to be expanded or not. At that moment the type can be in its original form |
* so yield to same types when it should be expanded. |
* |
* An alternative could be to remove the cashing from TypeUtil as anyway it is mainly used only during |
* construction of Converters (that are cached). However this might decrease performances on android, where you might |
* be more interested in one short ser/deser. Lets keep it like that for the moment. |
* |
* http://code.google.com/p/genson/issues/detail?id=4 |
*/ |
private final static class TypeAndRootClassKey { |
private final Type type; |
private final Type rootType; |
private int _hash; |
public TypeAndRootClassKey(Type type, Type rootType) { |
super(); |
if (type == null || rootType == null) |
throw new IllegalArgumentException("type and rootType must be not null!"); |
this.type = type; |
this.rootType = rootType; |
_hash = 31 + rootType.hashCode(); |
_hash = 31 + type.hashCode(); |
} |
@Override |
public int hashCode() { |
return _hash; |
} |
@Override |
public boolean equals(Object obj) { |
if (this == obj) |
return true; |
if (obj == null) |
return false; |
if (!(obj instanceof TypeAndRootClassKey)) |
return false; |
TypeAndRootClassKey other = (TypeAndRootClassKey) obj; |
return rootType.equals(other.rootType) && type.equals(other.type); |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/package-info.java |
---|
New file |
0,0 → 1,4 |
/** |
* This package contains the core api providing databinding support for complex objects. |
*/ |
package com.owlike.genson.reflect; |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/BeanCreator.java |
---|
New file |
0,0 → 1,214 |
package com.owlike.genson.reflect; |
import java.lang.annotation.Annotation; |
import java.lang.reflect.AnnotatedElement; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.InvocationTargetException; |
import java.lang.reflect.Method; |
import java.lang.reflect.Modifier; |
import java.lang.reflect.Type; |
import java.util.HashMap; |
import java.util.LinkedHashMap; |
import java.util.List; |
import java.util.Map; |
import com.owlike.genson.JsonBindingException; |
import com.owlike.genson.Wrapper; |
public abstract class BeanCreator extends Wrapper<AnnotatedElement> implements Comparable<BeanCreator> { |
// The type of object it can create |
protected final Class<?> ofClass; |
protected final Map<String, BeanCreatorProperty> parameters; |
protected final Map<String, BeanCreatorProperty> paramsAndAliases; |
public BeanCreator(Class<?> ofClass, Class<?> declaringClass, Class<?> concreteClass, |
String[] parameterNames, Type[] types, Annotation[][] anns) { |
this.ofClass = ofClass; |
this.parameters = new LinkedHashMap<String, BeanCreatorProperty>(parameterNames.length); |
for (int i = 0; i < parameterNames.length; i++) { |
this.parameters.put(parameterNames[i], new BeanCreatorProperty(parameterNames[i], |
types[i], i, anns[i], declaringClass, concreteClass, this)); |
} |
paramsAndAliases = new LinkedHashMap<String, BeanCreatorProperty>(); |
for (BeanCreatorProperty p : parameters.values()) { |
paramsAndAliases.put(p.getName(), p); |
for (String alias : p.aliases()) paramsAndAliases.put(alias, p); |
} |
} |
public int contains(List<String> properties) { |
int cnt = 0; |
for (String prop : properties) |
if (parameters.containsKey(prop)) cnt++; |
return cnt; |
} |
public int compareTo(BeanCreator o) { |
int comp = o.priority() - priority(); |
return comp != 0 ? comp : parameters.size() - o.parameters.size(); |
} |
public abstract Object create(Object... args); |
protected abstract String signature(); |
public abstract int priority(); |
protected JsonBindingException couldNotCreate(Exception e) { |
return new JsonBindingException("Could not create bean of type " + ofClass.getName() |
+ " using creator " + signature(), e); |
} |
public Map<String, BeanCreatorProperty> getProperties() { |
return new LinkedHashMap<String, BeanCreatorProperty>(parameters); |
} |
public abstract int getModifiers(); |
public static class ConstructorBeanCreator extends BeanCreator { |
protected final Constructor<?> constructor; |
public ConstructorBeanCreator(Class<?> ofClass, Constructor<?> constructor, |
String[] parameterNames, Type[] expandedParameterTypes) { |
// We can use the same class, as anyway we don't want to use constructors |
super(ofClass, ofClass, ofClass, parameterNames, expandedParameterTypes, constructor.getParameterAnnotations()); |
this.constructor = constructor; |
if (!constructor.isAccessible()) { |
constructor.setAccessible(true); |
} |
decorate(constructor); |
} |
public Object create(Object... args) { |
try { |
return constructor.newInstance(args); |
} catch (IllegalArgumentException e) { |
throw couldNotCreate(e); |
} catch (InstantiationException e) { |
throw couldNotCreate(e); |
} catch (IllegalAccessException e) { |
throw couldNotCreate(e); |
} catch (InvocationTargetException e) { |
throw couldNotCreate(e); |
} |
} |
@Override |
protected String signature() { |
return constructor.toGenericString(); |
} |
@Override |
public int priority() { |
return 50; |
} |
@Override |
public int getModifiers() { |
return constructor.getModifiers(); |
} |
} |
public static class MethodBeanCreator extends BeanCreator { |
protected final Method _creator; |
public MethodBeanCreator(Method method, String[] parameterNames, |
Type[] expandedParameterTypes, Class<?> concreteClass) { |
super(method.getReturnType(), method.getDeclaringClass(), concreteClass, parameterNames, |
expandedParameterTypes, method.getParameterAnnotations()); |
if (!Modifier.isStatic(method.getModifiers())) |
throw new IllegalStateException("Only static methods can be used as creators!"); |
this._creator = method; |
if (!_creator.isAccessible()) { |
_creator.setAccessible(true); |
} |
decorate(_creator); |
} |
public Object create(Object... args) { |
try { |
// we will handle only static method creators |
return ofClass.cast(_creator.invoke(null, args)); |
} catch (IllegalArgumentException e) { |
throw couldNotCreate(e); |
} catch (IllegalAccessException e) { |
throw couldNotCreate(e); |
} catch (InvocationTargetException e) { |
throw couldNotCreate(e); |
} |
} |
@Override |
protected String signature() { |
return _creator.toGenericString(); |
} |
@Override |
public int priority() { |
return 100; |
} |
@Override |
public int getModifiers() { |
return _creator.getModifiers(); |
} |
} |
public static class BeanCreatorProperty extends PropertyMutator { |
protected final int index; |
protected final Annotation[] annotations; |
protected final BeanCreator creator; |
protected final boolean doThrowMutateException; |
protected BeanCreatorProperty(String name, Type type, int index, Annotation[] annotations, |
Class<?> declaringClass, Class<?> concreteClass, BeanCreator creator) { |
this(name, type, index, annotations, declaringClass, concreteClass, creator, false); |
} |
protected BeanCreatorProperty(String name, Type type, int index, Annotation[] annotations, |
Class<?> declaringClass, Class<?> concreteClass, BeanCreator creator, |
boolean doThrowMutateException) { |
super(name, type, declaringClass, concreteClass, annotations, 0); |
this.index = index; |
this.annotations = annotations; |
this.creator = creator; |
this.doThrowMutateException = doThrowMutateException; |
} |
public int getIndex() { |
return index; |
} |
public Annotation[] getAnnotations() { |
return annotations; |
} |
@Override |
public int priority() { |
return -1000; |
} |
@Override |
public String signature() { |
return new StringBuilder(type.toString()).append(' ').append(name).append(" from ") |
.append(creator.signature()).toString(); |
} |
@Override |
public int getModifiers() { |
return creator.getModifiers(); |
} |
@Override |
public void mutate(Object target, Object value) { |
if (doThrowMutateException) { |
throw new IllegalStateException( |
"Method mutate should not be called on a mutator of type " |
+ getClass().getName() |
+ ", this property exists only as constructor parameter!"); |
} |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/BeanMutatorAccessorResolver.java |
---|
New file |
0,0 → 1,338 |
package com.owlike.genson.reflect; |
import java.lang.reflect.AccessibleObject; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.Field; |
import java.lang.reflect.Method; |
import java.lang.reflect.Modifier; |
import java.util.Arrays; |
import java.util.Iterator; |
import java.util.LinkedList; |
import java.util.List; |
import static com.owlike.genson.Trilean.FALSE; |
import static com.owlike.genson.Trilean.TRUE; |
import com.owlike.genson.JsonBindingException; |
import com.owlike.genson.Trilean; |
import com.owlike.genson.annotation.JsonCreator; |
import com.owlike.genson.annotation.JsonIgnore; |
import com.owlike.genson.annotation.JsonProperty; |
/** |
* BeanMutatorAccessorResolver interface must be implemented by class who want to resolve mutators |
* (fields or methods that allow you to modify a property), accessors (fields or methods that allow |
* you to retrieve the value of a property) and creators (constructors or static methods that allow |
* you to create objects). |
* <p/> |
* All methods return a {@link com.owlike.genson.Trilean Trilean}, so it may be TRUE, FALSE or UNKNOWN. |
* This will allow us to separate the kind of information each implementation works on (one for |
* annotations, another for visibility, etc) and to chain them. It also allows an easier addition of |
* new features without modifying the existing code. Have a look at <a href= |
* "http://code.google.com/p/genson/source/browse/src/main/java/com/owlike/genson/reflect/BeanMutatorAccessorResolver.java" |
* >StandardMutaAccessorResolver</a> for an example of BeanMutatorAccessorResolver implementation. |
* <p/> |
* To register your own implementation instead of the one by default use the genson builder. |
* <p/> |
* <pre> |
* new Genson.Builder().set(yourImplementation).create(); |
* </pre> |
* |
* @author eugen |
* @see com.owlike.genson.Trilean Trilean |
* @see StandardMutaAccessorResolver |
* @see AbstractBeanDescriptorProvider |
* @see BaseBeanDescriptorProvider |
* @see BeanViewDescriptorProvider |
*/ |
public interface BeanMutatorAccessorResolver { |
Trilean isCreator(Constructor<?> constructor, Class<?> fromClass); |
Trilean isCreator(Method method, Class<?> fromClass); |
Trilean isAccessor(Field field, Class<?> fromClass); |
Trilean isAccessor(Method method, Class<?> fromClass); |
Trilean isMutator(Field field, Class<?> fromClass); |
Trilean isMutator(Method method, Class<?> fromClass); |
class PropertyBaseResolver implements BeanMutatorAccessorResolver { |
@Override |
public Trilean isAccessor(Field field, Class<?> fromClass) { |
return Trilean.UNKNOWN; |
} |
@Override |
public Trilean isAccessor(Method method, Class<?> fromClass) { |
return Trilean.UNKNOWN; |
} |
@Override |
public Trilean isCreator(Constructor<?> constructor, Class<?> fromClass) { |
return Trilean.UNKNOWN; |
} |
@Override |
public Trilean isCreator(Method method, Class<?> fromClass) { |
return Trilean.UNKNOWN; |
} |
@Override |
public Trilean isMutator(Field field, Class<?> fromClass) { |
return Trilean.UNKNOWN; |
} |
@Override |
public Trilean isMutator(Method method, Class<?> fromClass) { |
return Trilean.UNKNOWN; |
} |
} |
class CompositeResolver implements BeanMutatorAccessorResolver { |
private List<BeanMutatorAccessorResolver> components; |
public CompositeResolver(List<BeanMutatorAccessorResolver> components) { |
if (components == null || components.isEmpty()) { |
throw new IllegalArgumentException( |
"The composite resolver must have at least one resolver as component!"); |
} |
this.components = new LinkedList<BeanMutatorAccessorResolver>(components); |
} |
public CompositeResolver add(BeanMutatorAccessorResolver... resolvers) { |
components.addAll(0, Arrays.asList(resolvers)); |
return this; |
} |
@Override |
public Trilean isAccessor(Field field, Class<?> fromClass) { |
Trilean resolved = Trilean.UNKNOWN; |
for (Iterator<BeanMutatorAccessorResolver> it = components.iterator(); resolved == null || resolved.equals(Trilean.UNKNOWN) |
&& it.hasNext(); ) { |
resolved = it.next().isAccessor(field, fromClass); |
} |
return resolved; |
} |
@Override |
public Trilean isAccessor(Method method, Class<?> fromClass) { |
Trilean resolved = Trilean.UNKNOWN; |
for (Iterator<BeanMutatorAccessorResolver> it = components.iterator(); resolved == null || resolved.equals(Trilean.UNKNOWN) |
&& it.hasNext(); ) { |
resolved = it.next().isAccessor(method, fromClass); |
} |
return resolved; |
} |
@Override |
public Trilean isCreator(Constructor<?> constructor, Class<?> fromClass) { |
Trilean resolved = Trilean.UNKNOWN; |
for (Iterator<BeanMutatorAccessorResolver> it = components.iterator(); resolved == null || resolved.equals(Trilean.UNKNOWN) |
&& it.hasNext(); ) { |
resolved = it.next().isCreator(constructor, fromClass); |
} |
return resolved; |
} |
@Override |
public Trilean isCreator(Method method, Class<?> fromClass) { |
Trilean resolved = Trilean.UNKNOWN; |
for (Iterator<BeanMutatorAccessorResolver> it = components.iterator(); resolved == null || resolved.equals(Trilean.UNKNOWN) |
&& it.hasNext(); ) { |
resolved = it.next().isCreator(method, fromClass); |
} |
return resolved; |
} |
@Override |
public Trilean isMutator(Field field, Class<?> fromClass) { |
Trilean resolved = Trilean.UNKNOWN; |
for (Iterator<BeanMutatorAccessorResolver> it = components.iterator(); resolved == null || resolved.equals(Trilean.UNKNOWN) |
&& it.hasNext(); ) { |
resolved = it.next().isMutator(field, fromClass); |
} |
return resolved; |
} |
@Override |
public Trilean isMutator(Method method, Class<?> fromClass) { |
Trilean resolved = Trilean.UNKNOWN; |
for (Iterator<BeanMutatorAccessorResolver> it = components.iterator(); resolved == null || resolved.equals(Trilean.UNKNOWN) |
&& it.hasNext(); ) { |
resolved = it.next().isMutator(method, fromClass); |
} |
return resolved; |
} |
} |
class GensonAnnotationsResolver implements BeanMutatorAccessorResolver { |
public Trilean isAccessor(Field field, Class<?> fromClass) { |
// ok to look for this$ is ugly but it will do the job for the moment |
if (mustIgnore(field, true) || field.getName().startsWith("this$")) |
return FALSE; |
if (mustInclude(field, true)) |
return TRUE; |
return Trilean.UNKNOWN; |
} |
public Trilean isAccessor(Method method, Class<?> fromClass) { |
if (mustIgnore(method, true)) |
return FALSE; |
if (mustInclude(method, true) && method.getParameterTypes().length == 0) |
return TRUE; |
return Trilean.UNKNOWN; |
} |
public Trilean isCreator(Constructor<?> constructor, Class<?> fromClass) { |
/* |
* hum... it depends on different things, such as parameters name resolution, types, etc |
* but we are not supposed to handle it here... lets only check visibility and handle it |
* in the provider implementations |
*/ |
if (mustIgnore(constructor, false)) |
return FALSE; |
return Trilean.UNKNOWN; |
} |
public Trilean isCreator(Method method, Class<?> fromClass) { |
if (method.getAnnotation(JsonCreator.class) != null) { |
if (Modifier.isPublic(method.getModifiers()) |
&& Modifier.isStatic(method.getModifiers())) |
return TRUE; |
throw new JsonBindingException("Method " + method.toGenericString() |
+ " annotated with @JsonCreator must be static!"); |
} |
return FALSE; |
} |
public Trilean isMutator(Field field, Class<?> fromClass) { |
if (mustIgnore(field, false) || field.getName().startsWith("this$")) |
return FALSE; |
if (mustInclude(field, false)) |
return TRUE; |
return Trilean.UNKNOWN; |
} |
public Trilean isMutator(Method method, Class<?> fromClass) { |
if (mustIgnore(method, false)) |
return FALSE; |
if (mustInclude(method, false) && method.getParameterTypes().length == 1) |
return TRUE; |
return Trilean.UNKNOWN; |
} |
protected boolean mustIgnore(AccessibleObject property, boolean forSerialization) { |
JsonIgnore ignore = property.getAnnotation(JsonIgnore.class); |
if (ignore != null) { |
if (forSerialization) |
return !ignore.serialize(); |
else |
return !ignore.deserialize(); |
} |
return false; |
} |
protected boolean mustInclude(AccessibleObject property, boolean forSerialization) { |
JsonProperty prop = property.getAnnotation(JsonProperty.class); |
if (prop != null) { |
if (forSerialization) |
return prop.serialize(); |
else |
return prop.deserialize(); |
} |
return false; |
} |
} |
/** |
* Standard implementation of BeanMutatorAccessorResolver. |
* Actually this implementation handles filtering by signature conventions (Java Bean) and visibility. |
* |
* @author eugen |
*/ |
class StandardMutaAccessorResolver implements BeanMutatorAccessorResolver { |
private final VisibilityFilter fieldVisibilityFilter; |
private final VisibilityFilter methodVisibilityFilter; |
private final VisibilityFilter creatorVisibilityFilter; |
/** |
* Creates a new instance of StandardMutaAccessorResolver with |
* {@link VisibilityFilter#PACKAGE_PUBLIC} visibility for fields, |
* {@link VisibilityFilter#PACKAGE_PUBLIC} visibility for methods and creators. |
*/ |
public StandardMutaAccessorResolver() { |
this(VisibilityFilter.PACKAGE_PUBLIC, VisibilityFilter.PACKAGE_PUBLIC, |
VisibilityFilter.PACKAGE_PUBLIC); |
} |
/** |
* Use this constructor if you want to customize the visibility filtering. |
* |
* @param filedVisibilityFilter |
* @param methodVisibilityFilter |
* @param creatorVisibilityFilter |
*/ |
public StandardMutaAccessorResolver(VisibilityFilter filedVisibilityFilter, |
VisibilityFilter methodVisibilityFilter, VisibilityFilter creatorVisibilityFilter) { |
super(); |
this.fieldVisibilityFilter = filedVisibilityFilter; |
this.methodVisibilityFilter = methodVisibilityFilter; |
this.creatorVisibilityFilter = creatorVisibilityFilter; |
} |
/** |
* Will resolve all public/package and non transient/static fields as accesssors. |
*/ |
public Trilean isAccessor(Field field, Class<?> fromClass) { |
return Trilean.valueOf(fieldVisibilityFilter.isVisible(field)); |
} |
/** |
* Resolves all public methods starting with get/is (boolean) and parameter less as |
* accessors. |
*/ |
public Trilean isAccessor(Method method, Class<?> fromClass) { |
if (!method.isBridge()) { |
String name = method.getName(); |
int len = name.length(); |
if (methodVisibilityFilter.isVisible(method) |
&& ((len > 3 && name.startsWith("get")) || (len > 2 && name.startsWith("is") && (TypeUtil |
.match(TypeUtil.expandType(method.getGenericReturnType(), fromClass), |
Boolean.class, false) || TypeUtil.match( |
method.getGenericReturnType(), boolean.class, false)))) |
&& method.getParameterTypes().length == 0) |
return TRUE; |
} |
return FALSE; |
} |
public Trilean isCreator(Constructor<?> constructor, Class<?> fromClass) { |
return Trilean.valueOf(creatorVisibilityFilter.isVisible(constructor)); |
} |
public Trilean isCreator(Method method, Class<?> fromClass) { |
return FALSE; |
} |
public Trilean isMutator(Field field, Class<?> fromClass) { |
return Trilean.valueOf(fieldVisibilityFilter.isVisible(field)); |
} |
public Trilean isMutator(Method method, Class<?> fromClass) { |
if (!method.isBridge()) { |
if (methodVisibilityFilter.isVisible(method) && method.getName().length() > 3 |
&& method.getName().startsWith("set") && method.getParameterTypes().length == 1 |
&& method.getReturnType() == void.class) |
return TRUE; |
} |
return FALSE; |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/RenamingPropertyNameResolver.java |
---|
New file |
0,0 → 1,63 |
package com.owlike.genson.reflect; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.Field; |
import java.lang.reflect.Method; |
public class RenamingPropertyNameResolver implements PropertyNameResolver { |
private final String field; |
private final Class<?> fromClass; |
private final Class<?> ofType; |
private final String toName; |
public RenamingPropertyNameResolver(String field, Class<?> fromClass, Class<?> ofType, String toName) { |
this.field = field; |
this.fromClass = fromClass; |
this.ofType = ofType; |
this.toName = toName; |
} |
@Override |
public String resolve(int parameterIdx, Constructor<?> fromConstructor) { |
return null; |
} |
@Override |
public String resolve(int parameterIdx, Method fromMethod) { |
return null; |
} |
@Override |
public String resolve(Field fromField) { |
return tryToRename(fromField.getName(), fromField.getDeclaringClass(), |
fromField.getType()); |
} |
@Override |
public String resolve(Method fromMethod) { |
String name = fromMethod.getName(); |
if (name.startsWith("is") && name.length() > 2) { |
return tryToRename(name.substring(2), fromMethod.getDeclaringClass(), |
fromMethod.getReturnType()); |
} |
if (name.length() > 3) { |
if (name.startsWith("get")) |
return tryToRename(name.substring(3), fromMethod.getDeclaringClass(), |
fromMethod.getReturnType()); |
if (name.startsWith("set") && fromMethod.getParameterTypes().length == 1) |
return tryToRename(name.substring(3), fromMethod.getDeclaringClass(), |
fromMethod.getParameterTypes()[0]); |
} |
return null; |
} |
private String tryToRename(String actualName, Class<?> declaringClass, |
Class<?> propertyType) { |
if ((field == null || actualName.equalsIgnoreCase(field)) |
&& (fromClass == null || fromClass.isAssignableFrom(declaringClass)) |
&& (ofType == null || ofType.isAssignableFrom(propertyType))) |
return toName; |
return null; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/PropertyFilter.java |
---|
New file |
0,0 → 1,65 |
package com.owlike.genson.reflect; |
import com.owlike.genson.Trilean; |
import java.lang.reflect.Field; |
import java.lang.reflect.Method; |
public class PropertyFilter extends BeanMutatorAccessorResolver.PropertyBaseResolver { |
private final boolean exclude; |
private final String field; |
private final Class<?> declaringClass; |
private final Class<?> ofType; |
public PropertyFilter(boolean exclude, String field, Class<?> declaringClass, Class<?> ofType) { |
this.exclude = exclude; |
this.field = field; |
this.declaringClass = declaringClass; |
this.ofType = ofType; |
} |
@Override |
public Trilean isAccessor(Field field, Class<?> fromClass) { |
return filter(field.getName(), fromClass, field.getType(), exclude); |
} |
@Override |
public Trilean isMutator(Field field, Class<?> fromClass) { |
return filter(field.getName(), fromClass, field.getType(), exclude); |
} |
@Override |
public Trilean isAccessor(Method method, Class<?> fromClass) { |
String name = method.getName(); |
if (name.startsWith("is") && name.length() > 2) { |
return filter(name.substring(2), method.getDeclaringClass(), |
method.getReturnType(), exclude); |
} |
if (name.length() > 3) { |
if (name.startsWith("get")) |
return filter(name.substring(3), method.getDeclaringClass(), |
method.getReturnType(), exclude); |
} |
return Trilean.UNKNOWN; |
} |
@Override |
public Trilean isMutator(Method method, Class<?> fromClass) { |
String name = method.getName(); |
if (name.length() > 3 && method.getParameterTypes().length == 1) { |
if (name.startsWith("set")) |
return filter(name.substring(3), method.getDeclaringClass(), |
method.getParameterTypes()[0], exclude); |
} |
return Trilean.UNKNOWN; |
} |
private Trilean filter(String actualName, Class<?> fromClass, |
Class<?> propertyType, boolean exclude) { |
if ((field == null || actualName.equalsIgnoreCase(field)) |
&& (declaringClass == null || declaringClass.isAssignableFrom(fromClass)) |
&& (ofType == null || ofType.isAssignableFrom(TypeUtil.wrap(propertyType)))) |
return exclude ? Trilean.FALSE : Trilean.TRUE; |
return Trilean.UNKNOWN; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/VisibilityFilter.java |
---|
New file |
0,0 → 1,79 |
package com.owlike.genson.reflect; |
import java.lang.reflect.Member; |
import java.lang.reflect.Modifier; |
/** |
* This class is used as filter for properties discovery. It uses java modifiers to check if a |
* property (can be a method, field, constructor or any class that implements Member interface) is |
* visible. |
* <p/> |
* The filter acts by excluding the properties with specified modifiers. Here are some examples : |
* <p/> |
* <pre> |
* // will filter nothing : |
* new VisibilityFilter(); |
* |
* // exclude only private and transient: |
* new VisibilityFilter(Modifier.TRANSIENT, Modifier.PRIVATE); |
* |
* // exclude only public!! and allow all the rest |
* new VisibilityFilter(Modifier.public); |
* </pre> |
* <p/> |
* So the idea is to pass to the constructor all the Modifier.XXX modifiers that you want to be |
* filtered. |
* |
* @author eugen |
* @see BeanMutatorAccessorResolver.StandardMutaAccessorResolver |
*/ |
public final class VisibilityFilter { |
private final static int JAVA_MODIFIERS = Modifier.PUBLIC | Modifier.PROTECTED |
| Modifier.PRIVATE | Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL |
| Modifier.TRANSIENT | Modifier.VOLATILE | Modifier.SYNCHRONIZED | Modifier.NATIVE |
| Modifier.STRICT | Modifier.INTERFACE; |
public final static VisibilityFilter ABSTRACT = new VisibilityFilter(Modifier.ABSTRACT); |
public final static VisibilityFilter PRIVATE = new VisibilityFilter(Modifier.TRANSIENT, |
Modifier.NATIVE, Modifier.STATIC); |
public final static VisibilityFilter ALL = new VisibilityFilter(); |
public final static VisibilityFilter NONE = new VisibilityFilter(JAVA_MODIFIERS); |
public final static VisibilityFilter PROTECTED = new VisibilityFilter(Modifier.TRANSIENT, |
Modifier.NATIVE, Modifier.STATIC, Modifier.PRIVATE); |
public final static VisibilityFilter PACKAGE_PUBLIC = new VisibilityFilter(Modifier.TRANSIENT, |
Modifier.NATIVE, Modifier.STATIC, Modifier.PRIVATE, Modifier.PROTECTED); |
private int filter; |
/** |
* Creates a new VisibilityFilter with specified modifiers. You must use existing values from |
* Modifier class otherwise an exception will be thrown. |
* |
* @param modifier all the modifiers you want to exclude. |
*/ |
public VisibilityFilter(int... modifier) { |
filter = 0; |
for (int m : modifier) { |
if ((m & JAVA_MODIFIERS) == 0) |
throw new IllegalArgumentException( |
"One of the modifiers is not a standard java modifier."); |
filter = filter | m; |
} |
} |
/** |
* Checks whether this member is visible or not according to this filter. |
* |
* @param member |
* @return true if this member is visible according to this filter. |
*/ |
public final boolean isVisible(Member member) { |
return isVisible(member.getModifiers()); |
} |
public final boolean isVisible(int modifiers) { |
return (modifiers & filter) == 0; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/reflect/RuntimePropertyFilter.java |
---|
New file |
0,0 → 1,15 |
package com.owlike.genson.reflect; |
import com.owlike.genson.Context; |
public interface RuntimePropertyFilter { |
RuntimePropertyFilter noFilter = new RuntimePropertyFilter() { |
@Override |
public boolean shouldInclude(BeanProperty property, Context ctx) { |
return true; |
} |
}; |
boolean shouldInclude(BeanProperty property, Context ctx); |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/Converter.java |
---|
New file |
0,0 → 1,45 |
package com.owlike.genson; |
import java.io.IOException; |
import com.owlike.genson.stream.ObjectReader; |
/** |
* Converter interface is a shorthand for classes who want to implement both serialization and |
* deserialization. You should always privilege Converter instead of low level Serializer and |
* Deseriliazer as they will be wrapped into a converter and all the ChainedFactory mechanism is |
* designed for converters. Here is an example of a Converter of URLs. |
* <p/> |
* <pre> |
* Genson genson = new Genson.Builder().with(new Converter<URL>() { |
* |
* @Override |
* public void serialize(URL url, ObjectWriter writer, Context ctx) { |
* // you don't have to worry about null objects, as the library will handle them. |
* writer.writeValue(obj.toExternalForm()); |
* } |
* |
* @Override |
* public URL deserialize(ObjectReader reader, Context ctx) { |
* return new URL(reader.valueAsString()); |
* } |
* |
* }).create(); |
* |
* String serializedUrl = genson.serialize(new URL("http://www.google.com")); |
* URL url = genson.deserialize(serializedUrl, URL.class); |
* </pre> |
* <p/> |
* As you can see it is quite straightforward to create and register new Converters. Here is an |
* example dealing with more complex objects. |
* |
* @param <T> type of objects handled by this converter. |
* @author eugen |
*/ |
public interface Converter<T> extends Serializer<T>, Deserializer<T> { |
@Override |
public void serialize(T object, com.owlike.genson.stream.ObjectWriter writer, Context ctx) throws Exception; |
@Override |
public T deserialize(ObjectReader reader, Context ctx) throws Exception; |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/Factory.java |
---|
New file |
0,0 → 1,71 |
package com.owlike.genson; |
import java.lang.reflect.Type; |
/** |
* Factory interface must be implemented by classes who want to act as factories and create |
* instances of Converter/Serializer/Deserializer. Implementations will be used as Converter, |
* Serializer and Deserializer factories. So the type T will be something like |
* Converter<Integer> but the type argument of method create will correspond to Integer <u>or |
* a subclass of Integer</u>. |
* <p/> |
* As an example you can have a look at factories from {@link com.owlike.genson.convert.DefaultConverters |
* DefaultConverters}. Here is an example with a custom converter and factory for enums. |
* <p/> |
* <pre> |
* public static class EnumConverter<T extends Enum<T>> implements Converter<T> { |
* private final Class<T> eClass; |
* |
* public EnumConverter(Class<T> eClass) { |
* this.eClass = eClass; |
* } |
* |
* @Override |
* public void serialize(T obj, ObjectWriter writer, Context ctx) { |
* writer.writeUnsafeValue(obj.name()); |
* } |
* |
* @Override |
* public T deserialize(ObjectReader reader, Context ctx) { |
* return Enum.valueOf(eClass, reader.valueAsString()); |
* } |
* } |
* |
* public final static class EnumConverterFactory implements Factory<Converter<? extends Enum<?>>> { |
* public final static EnumConverterFactory instance = new EnumConverterFactory(); |
* |
* private EnumConverterFactory() { |
* } |
* |
* @SuppressWarnings({ "rawtypes", "unchecked" }) |
* @Override |
* public Converter<Enum<?>> create(Type type, Genson genson) { |
* Class<?> rawClass = TypeUtil.getRawClass(type); |
* return rawClass.isEnum() || Enum.class.isAssignableFrom(rawClass) ? new EnumConverter( |
* rawClass) : null; |
* } |
* }; |
* </pre> |
* <p/> |
* Note the use of {@link com.owlike.genson.reflect.TypeUtil TypeUtil} class that provides operations to |
* work with generic types. However this class might change in the future, in order to provide a better API. |
* |
* @param <T> the base type of the objects this factory can create. T can be of type Converter, |
* Serializer or Deserializer. |
* @author eugen |
* @see com.owlike.genson.Converter |
* @see com.owlike.genson.convert.ChainedFactory ChainedFactory |
* @see com.owlike.genson.Serializer |
* @see com.owlike.genson.Deserializer |
*/ |
public interface Factory<T> { |
/** |
* Implementations of this method must try to create an instance of type T based on the |
* parameter "type". If this factory can not create an object of type T for parameter type then |
* it must return null. |
* |
* @param type used to build an instance of T. |
* @return null if it doesn't support this type or an instance of T (or a subclass). |
*/ |
public T create(Type type, Genson genson); |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/Serializer.java |
---|
New file |
0,0 → 1,69 |
package com.owlike.genson; |
import java.io.IOException; |
import com.owlike.genson.stream.ObjectWriter; |
/** |
* Serializers handle serialization by writing a java object of type T to a stream using |
* {@link com.owlike.genson.stream.ObjectWriter ObjectWriter}. Genson Serializers work like classic |
* serializers from other libraries. Here is an example of a custom serializer that will delegate |
* the serialization of Author type to the library: |
* <p/> |
* <pre> |
* class Book { |
* String title; |
* int totalPages; |
* Author author; |
* } |
* |
* class Author { |
* String name; |
* } |
* |
* static class BookSerializer implements Serializer<Book> { |
* private final Serializer<Author> authorSerializer; |
* |
* // a reference to a delegated author serializer |
* BookSerializer(Serializer<Author> authorSerializer) { |
* this.authorSerializer = authorSerializer; |
* } |
* |
* public void serialize(Book book, ObjectWriter writer, Context ctx) { |
* // we don't have to worry if book is null by default it is handled by the library. |
* writer.beginObject().writeName("title").writeValue(book.title).writeName("totalPages") |
* .writeValue(book.totalPages).writeName("author"); |
* |
* // again no need to check if author is null the library will handle it |
* authorSerializer.serialize(book.author, writer, ctx); |
* writer.endObject(); |
* } |
* |
* public final static Factory<Serializer<Book>> bookFactory = new Factory<Serializer<Book>>() { |
* |
* public Serializer<Book> create(Type type, Genson genson) { |
* Serializer<Author> authorSerializer = genson.provideConverter(Author.class); |
* return new GoodBookSerializer(authorSerializer); |
* } |
* } |
* } |
* </pre> |
* <p/> |
* As you see it involves very few lines of code and is quite powerful. |
* |
* @param <T> the type of objects this Serializer can serialize. |
* @author eugen |
* @see Converter |
* @see com.owlike.genson.Factory Factory |
*/ |
public interface Serializer<T> { |
/** |
* @param object we want to serialize. The object is of type T or a subclass (if this serializer |
* has been registered for subclasses). |
* @param writer to use to write data to the output stream. |
* @param ctx the current context. |
* @throws com.owlike.genson.JsonBindingException |
* @throws com.owlike.genson.stream.JsonStreamException |
*/ |
public void serialize(T object, ObjectWriter writer, Context ctx) throws Exception; |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/stream/ObjectWriter.java |
---|
New file |
0,0 → 1,311 |
package com.owlike.genson.stream; |
import java.io.Closeable; |
import java.io.Flushable; |
import java.io.IOException; |
/** |
* ObjectWriter defines the api allowing to write data to different format and the contract for |
* classes that implement ObjectWriter to provide different formats support. Implementations are |
* extremely efficient as they use low level stream operations and optimizations. If you want |
* optimal performance you can directly use the streaming api (ObjectWriter and {@link ObjectReader} |
* ) without the databind support that comes with the converters. This will be very close in terms |
* of performance as writing manually formated data to the stream. |
* <p/> |
* If you want to write the array new int[1, 2, 3] to the stream with ObjectWriter: |
* <p/> |
* <pre> |
* writer.beginArray().writeValue(1).writeValue(2).writeValue(3).endArray(); |
* </pre> |
* <p/> |
* And to write Person (we simplify, in practice you must handle null values): |
* <p/> |
* <pre> |
* class Person { |
* public static void main(String[] args) { |
* // we will write from Person to json string |
* Person p = new Person(); |
* p.name = "eugen"; |
* p.age = 26; |
* p.childrenYearOfBirth = new ArrayList<Integer>(); |
* StringWriter sw = new StringWriter(); |
* ObjectWriter writer = new JsonWriter(sw); |
* p.write(writer); |
* writer.flush(); |
* writer.close(); |
* // will write {"name":"eugen","age":26,"childrenYearOfBirth":[]} |
* System.out.println(sw.toString()); |
* } |
* |
* String name; |
* int age; |
* List<Integer> childrenYearOfBirth; |
* |
* public void write(ObjectWriter writer) { |
* writer.beginObject().writeName("name"); |
* if (name == null) writer.writeNull(); |
* else writer.writeValue(name) |
* writer.writeName("age").writeAge(age) |
* .writeName("childrenYearOfBirth"); |
* if (childrenYearOfBirth == null) writer.writeNull(); |
* else { |
* writer.beginArray(); |
* for (Integer year : childrenYearOfBirth) |
* writer.writeValue(year); |
* writer.endArray() |
* } |
* writer.endObject(); |
* } |
* } |
* </pre> |
* <p/> |
* Be careful if you instantiate ObjectWriter your self you are responsible of flushing and closing |
* the stream. |
* |
* @author eugen |
* @see JsonWriter |
* @see ObjectReader |
* @see JsonReader |
*/ |
public interface ObjectWriter { |
/** |
* Starts to write an array (use it also for collections). An array is a suite of values that |
* may be literals, arrays or objects. When you finished writing the values don't forget to call |
* endArray(). |
* |
* @return a reference to this allowing to chain method calls. |
* @throws JsonStreamException if trying to produce invalid json |
*/ |
public ObjectWriter beginArray(); |
/** |
* Ends the array, if beginArray was not called, implementations should throw an exception. |
* |
* @return a reference to this allowing to chain method calls. |
* @throws JsonStreamException if trying to produce invalid json |
*/ |
public ObjectWriter endArray(); |
/** |
* Starts a object, objects are a suite of name/value pairs, values may be literals, arrays or |
* objects. Don't forget to call endObject. |
* |
* @return a reference to this allowing to chain method calls. |
* @throws JsonStreamException if trying to produce invalid json |
*/ |
public ObjectWriter beginObject(); |
/** |
* Ends the object being written, if beginObject was not called an exception will be throwed. |
* |
* @return a reference to this allowing to chain method calls. |
* @throws JsonStreamException if trying to produce invalid json |
*/ |
public ObjectWriter endObject(); |
/** |
* Writes the name of a property. Names can be written only in objects and must be called before |
* writing the properties value. |
* |
* @param name a non null String |
* @return a reference to this, allowing to chain method calls. |
* @throws JsonStreamException if trying to produce invalid json |
*/ |
public ObjectWriter writeName(String name); |
/** |
* Will write the name without escaping special characters, assuming it has been done by the caller or the string |
* doesn't contain any character needing to be escaped. |
* @param name a non null escaped String |
* @return a reference to this, allowing to chain method calls. |
* @throws JsonStreamException if trying to produce invalid json |
*/ |
public ObjectWriter writeEscapedName(char[] name); |
/** |
* Writes a value to the stream. Values can be written in arrays and in objects (after writing |
* the name). |
* |
* @param value to write. |
* @return a reference to this, allowing to chain method calls. |
* @throws JsonStreamException if trying to produce invalid json |
*/ |
public ObjectWriter writeValue(int value); |
/** |
* See {@link #writeValue(int)}. |
* |
* @throws JsonStreamException if trying to produce invalid json |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeValue(double value); |
/** |
* See {@link #writeValue(int)}. |
* |
* @throws JsonStreamException if trying to produce invalid json |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeValue(long value); |
/** |
* See {@link #writeValue(int)}. |
* |
* @throws JsonStreamException if trying to produce invalid json |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeValue(short value); |
/** |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeValue(float value); |
/** |
* See {@link #writeValue(int)}. |
* |
* @throws JsonStreamException if trying to produce invalid json |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeValue(boolean value); |
/** |
* @see #writeString(String) |
*/ |
public ObjectWriter writeBoolean(Boolean value); |
/** |
* See {@link #writeValue(int)}. |
* |
* @throws JsonStreamException if trying to produce invalid json |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeValue(Number value); |
/** |
* @see #writeString(String) |
*/ |
public ObjectWriter writeNumber(Number value); |
/** |
* See {@link #writeValue(int)}. |
* |
* @throws JsonStreamException if trying to produce invalid json |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeValue(String value); |
/** |
* Similar to writeValue(String) but is null safe, meaning that if the value is null, |
* then the write will call writeNull for you. |
*/ |
public ObjectWriter writeString(String value); |
/** |
* Writes an array of bytes as a base64 encoded string. See {@link #writeValue(int)}. |
* |
* @throws JsonStreamException if trying to produce invalid json |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeValue(byte[] value); |
/** |
* @see #writeString(String) |
*/ |
public ObjectWriter writeBytes(byte[] value); |
/** |
* Writes value as is without any pre-processing, it's faster than {@link #writeValue(String)} |
* but should be used only if you know that it is safe. |
* |
* @throws JsonStreamException if trying to produce invalid json |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeUnsafeValue(String value); |
/** |
* Must be called when a null value is encountered. Implementations will deal with the null |
* representation (just skip it or write null, etc). |
* |
* @return a reference to this allowing to chain method calls. |
* @throws JsonStreamException if trying to produce invalid json |
* @see #writeValue(int) |
*/ |
public ObjectWriter writeNull(); |
/** |
* This method is a kind of cheat as it allows us to start writing metadata and then still be |
* able to call beginObject. This is mainly intended to be used in wrapped converters that want |
* to handle a part of the serialization and then let the chain continue. Have a look at <a |
* href= |
* "http://code.google.com/p/genson/source/browse/src/main/java/com/owlike/genson/convert/ClassMetadataConverter.java" |
* >ClassMetadataConverter</a> |
* |
* @return a reference to this allowing to chain method calls. |
* @throws JsonStreamException |
* @see #writeMetadata(String, String) |
*/ |
public ObjectWriter beginNextObjectMetadata(); |
/** |
* Metadata is a suite of name/value pairs, names will be prepended with '@' (handled by the |
* library). Metadata feature is experimental for the moment so things may change a bit. The |
* signature will not, but the way it is implemented could... Actually the contract is that |
* metadata must be written first and only in objects. If it does not respect these conditions |
* ObjectReader won't be able to detect it as metadata. Here is an example of two ways to write |
* object metadata. |
* <p/> |
* <pre> |
* // here it is transparent for the library if you write metadata or something else, you must call |
* // beginObject before being able to start writing its metadata. |
* writer.beginObject().writeMetadata("doc", "Object documentation bla bla...").writeName("name") |
* .writeNull().endObject().flush(); |
* |
* // previous example works fine, but if you want to write some metadata and still be able to call |
* // beginObject (for example in a converter you want to write some metadata and have all the existing |
* // continue to work with calling beginObject) you must use beginNextObjectMetadata. |
* |
* // written from a first converter |
* writer.beginNextObjectMetadata().writeMetadata("dataFromConverter1", "writtenFromConverter1"); |
* // written from a second converter after first one |
* writer.beginNextObjectMetadata().writeMetadata("dataFromConverter2", "writtenFromConverter2"); |
* // finally concrete properties will be written from a custom converter |
* writer.beginObject().writeName("name").writeNull().endObject().flush(); |
* </pre> |
* |
* @param name of the metadata property, should not start with '@', the library will add it. |
* @param value of the metadata property. |
* @return a reference to this allowing to chain method calls. |
* @throws JsonStreamException |
* @see #beginNextObjectMetadata() |
*/ |
public ObjectWriter writeMetadata(String name, String value); |
/** |
* @see #writeString(String, String) |
*/ |
public ObjectWriter writeBoolean(String name, Boolean value); |
/** |
* @see #writeString(String, String) |
*/ |
public ObjectWriter writeNumber(String name, Number value); |
/** |
* Will write the name and the value, it is just a shortcut for writer.writeName("key").writeString(value). |
* Note if the value is null, writeNull is used. |
*/ |
public ObjectWriter writeString(String name, String value); |
/** |
* @see #writeString(String, String) |
*/ |
public ObjectWriter writeBytes(String name, byte[] value); |
public void flush(); |
public void close(); |
public JsonType enclosingType(); |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/stream/Base64.java |
---|
New file |
0,0 → 1,585 |
package com.owlike.genson.stream; |
import java.util.Arrays; |
/** |
* A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance |
* with RFC 2045.<br><br> |
* On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is about 10 times faster |
* on small arrays (10 - 1000 bytes) and 2-3 times as fast on larger arrays (10000 - 1000000 bytes) |
* compared to <code>sun.misc.Encoder()/Decoder()</code>.<br><br> |
* <p/> |
* On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 Codec for encode and |
* about 50% faster for decoding large arrays. This implementation is about twice as fast on very small |
* arrays (< 30 bytes). If source/destination is a <code>String</code> this |
* version is about three times as fast due to the fact that the Commons Codec result has to be recoded |
* to a <code>String</code> from <code>byte[]</code>, which is very expensive.<br><br> |
* <p/> |
* This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only |
* allocates the resulting array. This produces less garbage and it is possible to handle arrays twice |
* as large as algorithms that create a temporary array. (E.g. Jakarta Commons Codec). It is unknown |
* whether Sun's <code>sun.misc.Encoder()/Decoder()</code> produce temporary arrays but since performance |
* is quite low it probably does.<br><br> |
* <p/> |
* The encoder produces the same output as the Sun one except that the Sun's encoder appends |
* a trailing line separator if the last character isn't a pad. Unclear why but it only adds to the |
* length and is probably a side effect. Both are in conformance with RFC 2045 though.<br> |
* Commons codec seem to always att a trailing line separator.<br><br> |
* <p/> |
* <b>Note!</b> |
* The encode/decode method pairs (types) come in three versions with the <b>exact</b> same algorithm and |
* thus a lot of code redundancy. This is to not create any temporary arrays for transcoding to/from different |
* format types. The methods not used can simply be commented out.<br><br> |
* <p/> |
* There is also a "fast" version of all decode methods that works the same way as the normal ones, but |
* har a few demands on the decoded input. Normally though, these fast verions should be used if the source if |
* the input is known and it hasn't bee tampered with.<br><br> |
* <p/> |
* If you find the code useful or you find a bug, please send me a note at base64 @ miginfocom . com. |
* <p/> |
* Licence (BSD): |
* ============== |
* <p/> |
* Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com) |
* All rights reserved. |
* <p/> |
* Redistribution and use in source and binary forms, with or without modification, |
* are permitted provided that the following conditions are met: |
* Redistributions of source code must retain the above copyright notice, this list |
* of conditions and the following disclaimer. |
* Redistributions in binary form must reproduce the above copyright notice, this |
* list of conditions and the following disclaimer in the documentation and/or other |
* materials provided with the distribution. |
* Neither the name of the MiG InfoCom AB nor the names of its contributors may be |
* used to endorse or promote products derived from this software without specific |
* prior written permission. |
* <p/> |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
* OF SUCH DAMAGE. |
* |
* @author Mikael Grev |
* Date: 2004-aug-02 |
* Time: 11:31:11 |
* @version 2.2 |
*/ |
class Base64 { |
private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); |
private static final int[] IA = new int[256]; |
static { |
Arrays.fill(IA, -1); |
for (int i = 0, iS = CA.length; i < iS; i++) |
IA[CA[i]] = i; |
IA['='] = 0; |
} |
// **************************************************************************************** |
// * char[] version |
// **************************************************************************************** |
/** |
* Encodes a raw byte array into a BASE64 <code>char[]</code> representation i accordance with RFC 2045. |
* |
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned. |
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br> |
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a |
* little faster. |
* @return A BASE64 encoded array. Never <code>null</code>. |
*/ |
public final static char[] encodeToChar(byte[] sArr, boolean lineSep) { |
// Check special case |
int sLen = sArr != null ? sArr.length : 0; |
if (sLen == 0) |
return new char[0]; |
int eLen = (sLen / 3) * 3; // Length of even 24-bits. |
int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count |
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array |
char[] dArr = new char[dLen]; |
// Encode even 24-bits |
for (int s = 0, d = 0, cc = 0; s < eLen; ) { |
// Copy next three bytes into lower 24 bits of int, paying attension to sign. |
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); |
// Encode the int into four chars |
dArr[d++] = CA[(i >>> 18) & 0x3f]; |
dArr[d++] = CA[(i >>> 12) & 0x3f]; |
dArr[d++] = CA[(i >>> 6) & 0x3f]; |
dArr[d++] = CA[i & 0x3f]; |
// Add optional line separator |
if (lineSep && ++cc == 19 && d < dLen - 2) { |
dArr[d++] = '\r'; |
dArr[d++] = '\n'; |
cc = 0; |
} |
} |
// Pad and encode last bits if source isn't even 24 bits. |
int left = sLen - eLen; // 0 - 2. |
if (left > 0) { |
// Prepare the int |
int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); |
// Set last four chars |
dArr[dLen - 4] = CA[i >> 12]; |
dArr[dLen - 3] = CA[(i >>> 6) & 0x3f]; |
dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '='; |
dArr[dLen - 1] = '='; |
} |
return dArr; |
} |
/** |
* Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with |
* and without line separators. |
* |
* @param sArr The source array. <code>null</code> or length 0 will return an empty array. |
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters |
* (including '=') isn't divideable by 4. (I.e. definitely corrupted). |
*/ |
public final static byte[] decode(char[] sArr) { |
// Check special case |
int sLen = sArr != null ? sArr.length : 0; |
if (sLen == 0) |
return new byte[0]; |
// Count illegal characters (including '\r', '\n') to know what size the returned array will be, |
// so we don't have to reallocate & copy it later. |
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) |
for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. |
if (IA[sArr[i]] < 0) |
sepCnt++; |
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. |
if ((sLen - sepCnt) % 4 != 0) |
return null; |
int pad = 0; |
for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0; ) |
if (sArr[i] == '=') |
pad++; |
int len = ((sLen - sepCnt) * 6 >> 3) - pad; |
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length |
for (int s = 0, d = 0; d < len; ) { |
// Assemble three bytes into an int from four "valid" characters. |
int i = 0; |
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. |
int c = IA[sArr[s++]]; |
if (c >= 0) |
i |= c << (18 - j * 6); |
else |
j--; |
} |
// Add the bytes |
dArr[d++] = (byte) (i >> 16); |
if (d < len) { |
dArr[d++] = (byte) (i >> 8); |
if (d < len) |
dArr[d++] = (byte) i; |
} |
} |
return dArr; |
} |
/** |
* Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as |
* fast as {@link #decode(char[])}. The preconditions are:<br> |
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br> |
* + Line separator must be "\r\n", as specified in RFC 2045 |
* + The array must not contain illegal characters within the encoded string<br> |
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br> |
* |
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception. |
* @return The decoded array of bytes. May be of length 0. |
*/ |
public final static byte[] decodeFast(char[] sArr) { |
// Check special case |
int sLen = sArr.length; |
if (sLen == 0) |
return new byte[0]; |
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. |
// Trim illegal chars from start |
while (sIx < eIx && IA[sArr[sIx]] < 0) |
sIx++; |
// Trim illegal chars from end |
while (eIx > 0 && IA[sArr[eIx]] < 0) |
eIx--; |
// get the padding count (=) (0, 1 or 2) |
int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. |
int cCnt = eIx - sIx + 1; // Content count including possible separators |
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; |
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes |
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length |
// Decode all but the last 0 - 2 bytes. |
int d = 0; |
for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { |
// Assemble three bytes into an int from four "valid" characters. |
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; |
// Add the bytes |
dArr[d++] = (byte) (i >> 16); |
dArr[d++] = (byte) (i >> 8); |
dArr[d++] = (byte) i; |
// If line separator, jump over it. |
if (sepCnt > 0 && ++cc == 19) { |
sIx += 2; |
cc = 0; |
} |
} |
if (d < len) { |
// Decode last 1-3 bytes (incl '=') into 1-3 bytes |
int i = 0; |
for (int j = 0; sIx <= eIx - pad; j++) |
i |= IA[sArr[sIx++]] << (18 - j * 6); |
for (int r = 16; d < len; r -= 8) |
dArr[d++] = (byte) (i >> r); |
} |
return dArr; |
} |
// **************************************************************************************** |
// * byte[] version |
// **************************************************************************************** |
/** |
* Encodes a raw byte array into a BASE64 <code>byte[]</code> representation i accordance with RFC 2045. |
* |
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned. |
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br> |
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a |
* little faster. |
* @return A BASE64 encoded array. Never <code>null</code>. |
*/ |
public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) { |
// Check special case |
int sLen = sArr != null ? sArr.length : 0; |
if (sLen == 0) |
return new byte[0]; |
int eLen = (sLen / 3) * 3; // Length of even 24-bits. |
int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count |
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array |
byte[] dArr = new byte[dLen]; |
// Encode even 24-bits |
for (int s = 0, d = 0, cc = 0; s < eLen; ) { |
// Copy next three bytes into lower 24 bits of int, paying attension to sign. |
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); |
// Encode the int into four chars |
dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; |
dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; |
dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; |
dArr[d++] = (byte) CA[i & 0x3f]; |
// Add optional line separator |
if (lineSep && ++cc == 19 && d < dLen - 2) { |
dArr[d++] = '\r'; |
dArr[d++] = '\n'; |
cc = 0; |
} |
} |
// Pad and encode last bits if source isn't an even 24 bits. |
int left = sLen - eLen; // 0 - 2. |
if (left > 0) { |
// Prepare the int |
int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); |
// Set last four chars |
dArr[dLen - 4] = (byte) CA[i >> 12]; |
dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; |
dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; |
dArr[dLen - 1] = '='; |
} |
return dArr; |
} |
/** |
* Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with |
* and without line separators. |
* |
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception. |
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters |
* (including '=') isn't divideable by 4. (I.e. definitely corrupted). |
*/ |
public final static byte[] decode(byte[] sArr) { |
// Check special case |
int sLen = sArr.length; |
// Count illegal characters (including '\r', '\n') to know what size the returned array will be, |
// so we don't have to reallocate & copy it later. |
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) |
for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. |
if (IA[sArr[i] & 0xff] < 0) |
sepCnt++; |
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. |
if ((sLen - sepCnt) % 4 != 0) |
return null; |
int pad = 0; |
for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0; ) |
if (sArr[i] == '=') |
pad++; |
int len = ((sLen - sepCnt) * 6 >> 3) - pad; |
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length |
for (int s = 0, d = 0; d < len; ) { |
// Assemble three bytes into an int from four "valid" characters. |
int i = 0; |
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. |
int c = IA[sArr[s++] & 0xff]; |
if (c >= 0) |
i |= c << (18 - j * 6); |
else |
j--; |
} |
// Add the bytes |
dArr[d++] = (byte) (i >> 16); |
if (d < len) { |
dArr[d++] = (byte) (i >> 8); |
if (d < len) |
dArr[d++] = (byte) i; |
} |
} |
return dArr; |
} |
/** |
* Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as |
* fast as {@link #decode(byte[])}. The preconditions are:<br> |
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br> |
* + Line separator must be "\r\n", as specified in RFC 2045 |
* + The array must not contain illegal characters within the encoded string<br> |
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br> |
* |
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception. |
* @return The decoded array of bytes. May be of length 0. |
*/ |
public final static byte[] decodeFast(byte[] sArr) { |
// Check special case |
int sLen = sArr.length; |
if (sLen == 0) |
return new byte[0]; |
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. |
// Trim illegal chars from start |
while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) |
sIx++; |
// Trim illegal chars from end |
while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) |
eIx--; |
// get the padding count (=) (0, 1 or 2) |
int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. |
int cCnt = eIx - sIx + 1; // Content count including possible separators |
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; |
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes |
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length |
// Decode all but the last 0 - 2 bytes. |
int d = 0; |
for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { |
// Assemble three bytes into an int from four "valid" characters. |
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; |
// Add the bytes |
dArr[d++] = (byte) (i >> 16); |
dArr[d++] = (byte) (i >> 8); |
dArr[d++] = (byte) i; |
// If line separator, jump over it. |
if (sepCnt > 0 && ++cc == 19) { |
sIx += 2; |
cc = 0; |
} |
} |
if (d < len) { |
// Decode last 1-3 bytes (incl '=') into 1-3 bytes |
int i = 0; |
for (int j = 0; sIx <= eIx - pad; j++) |
i |= IA[sArr[sIx++]] << (18 - j * 6); |
for (int r = 16; d < len; r -= 8) |
dArr[d++] = (byte) (i >> r); |
} |
return dArr; |
} |
// **************************************************************************************** |
// * String version |
// **************************************************************************************** |
/** |
* Encodes a raw byte array into a BASE64 <code>String</code> representation i accordance with RFC 2045. |
* |
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned. |
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br> |
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a |
* little faster. |
* @return A BASE64 encoded array. Never <code>null</code>. |
*/ |
public final static String encodeToString(byte[] sArr, boolean lineSep) { |
// Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower. |
return new String(encodeToChar(sArr, lineSep)); |
} |
/** |
* Decodes a BASE64 encoded <code>String</code>. All illegal characters will be ignored and can handle both strings with |
* and without line separators.<br> |
* <b>Note!</b> It can be up to about 2x the speed to call <code>decode(str.toCharArray())</code> instead. That |
* will create a temporary array though. This version will use <code>str.charAt(i)</code> to iterate the string. |
* |
* @param str The source string. <code>null</code> or length 0 will return an empty array. |
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters |
* (including '=') isn't divideable by 4. (I.e. definitely corrupted). |
*/ |
public final static byte[] decode(String str) { |
// Check special case |
int sLen = str != null ? str.length() : 0; |
if (sLen == 0) |
return new byte[0]; |
// Count illegal characters (including '\r', '\n') to know what size the returned array will be, |
// so we don't have to reallocate & copy it later. |
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) |
for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. |
if (IA[str.charAt(i)] < 0) |
sepCnt++; |
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. |
if ((sLen - sepCnt) % 4 != 0) |
return null; |
// Count '=' at end |
int pad = 0; |
for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0; ) |
if (str.charAt(i) == '=') |
pad++; |
int len = ((sLen - sepCnt) * 6 >> 3) - pad; |
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length |
for (int s = 0, d = 0; d < len; ) { |
// Assemble three bytes into an int from four "valid" characters. |
int i = 0; |
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. |
int c = IA[str.charAt(s++)]; |
if (c >= 0) |
i |= c << (18 - j * 6); |
else |
j--; |
} |
// Add the bytes |
dArr[d++] = (byte) (i >> 16); |
if (d < len) { |
dArr[d++] = (byte) (i >> 8); |
if (d < len) |
dArr[d++] = (byte) i; |
} |
} |
return dArr; |
} |
/** |
* Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as |
* fast as {@link #decode(String)}. The preconditions are:<br> |
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br> |
* + Line separator must be "\r\n", as specified in RFC 2045 |
* + The array must not contain illegal characters within the encoded string<br> |
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br> |
* |
* @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception. |
* @return The decoded array of bytes. May be of length 0. |
*/ |
public final static byte[] decodeFast(String s) { |
// Check special case |
int sLen = s.length(); |
if (sLen == 0) |
return new byte[0]; |
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. |
// Trim illegal chars from start |
while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) |
sIx++; |
// Trim illegal chars from end |
while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) |
eIx--; |
// get the padding count (=) (0, 1 or 2) |
int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end. |
int cCnt = eIx - sIx + 1; // Content count including possible separators |
int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; |
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes |
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length |
// Decode all but the last 0 - 2 bytes. |
int d = 0; |
for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { |
// Assemble three bytes into an int from four "valid" characters. |
int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)]; |
// Add the bytes |
dArr[d++] = (byte) (i >> 16); |
dArr[d++] = (byte) (i >> 8); |
dArr[d++] = (byte) i; |
// If line separator, jump over it. |
if (sepCnt > 0 && ++cc == 19) { |
sIx += 2; |
cc = 0; |
} |
} |
if (d < len) { |
// Decode last 1-3 bytes (incl '=') into 1-3 bytes |
int i = 0; |
for (int j = 0; sIx <= eIx - pad; j++) |
i |= IA[s.charAt(sIx++)] << (18 - j * 6); |
for (int r = 16; d < len; r -= 8) |
dArr[d++] = (byte) (i >> r); |
} |
return dArr; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/stream/JsonStreamException.java |
---|
New file |
0,0 → 1,82 |
package com.owlike.genson.stream; |
/** |
* JsonStreamException are thrown by ObjectWriter and ObjectReader implementations. They indicate |
* that there was a syntax error or a state error (calling endObject when it should be endArray |
* etc). |
* |
* @author eugen |
*/ |
public final class JsonStreamException extends RuntimeException { |
private static final long serialVersionUID = 8033784054415043293L; |
private final int column; |
private final int row; |
public JsonStreamException(String message, Throwable cause) { |
this(message, cause, -1, -1); |
} |
public JsonStreamException(String message) { |
this(message, null); |
} |
public JsonStreamException(Throwable cause) { |
this(null, cause); |
} |
// package visibility, api users are not supposed to use it |
JsonStreamException(String message, Throwable cause, int row, int col) { |
super(message, cause); |
this.column = col; |
this.row = row; |
} |
public int getColumn() { |
return column; |
} |
public int getRow() { |
return row; |
} |
public static <T extends Exception> T niceTrace(T exception) { |
final StackTraceElement[] stackTrace = exception.getStackTrace(); |
final StackTraceElement[] newStackTrace = new StackTraceElement[stackTrace.length - 1]; |
System.arraycopy(stackTrace, 1, newStackTrace, 0, stackTrace.length - 1); |
exception.setStackTrace(newStackTrace); |
return exception; |
} |
public JsonStreamException niceTrace() { |
return niceTrace(this); |
} |
static class Builder { |
private int col; |
private int row; |
private String message; |
private Throwable cause; |
public JsonStreamException create() { |
return new JsonStreamException(message, cause, row, col); |
} |
Builder locate(int row, int col) { |
this.row = row; |
this.col = col; |
return this; |
} |
public Builder message(String message) { |
this.message = message; |
return this; |
} |
public Builder cause(Throwable th) { |
this.cause = th; |
return this; |
} |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/stream/package-info.java |
---|
New file |
0,0 → 1,5 |
/** |
* This package provides the streaming api used to read and write to streams. |
* Actually only json format is supported however xml may be added latter ;) |
*/ |
package com.owlike.genson.stream; |
/branches/grupo4/impl/src/java/com/owlike/genson/stream/JsonWriter.java |
---|
New file |
0,0 → 1,578 |
package com.owlike.genson.stream; |
import java.io.IOException; |
import java.io.Writer; |
import java.util.ArrayDeque; |
import java.util.ArrayList; |
import java.util.Deque; |
import java.util.List; |
public class JsonWriter implements ObjectWriter { |
/* |
* TODO try to do something different and faster, optimize writeValue(String) |
*/ |
private final static char[][] REPLACEMENT_CHARS; |
private final static char[][] HTML_SAFE_REPLACEMENT_CHARS; |
static { |
REPLACEMENT_CHARS = new char[128][]; |
for (int i = 0; i <= 0x1f; i++) { |
REPLACEMENT_CHARS[i] = String.format("\\u%04x", (int) i).toCharArray(); |
} |
REPLACEMENT_CHARS['"'] = "\\\"".toCharArray(); |
REPLACEMENT_CHARS['\\'] = "\\\\".toCharArray(); |
REPLACEMENT_CHARS['\t'] = "\\t".toCharArray(); |
REPLACEMENT_CHARS['\b'] = "\\b".toCharArray(); |
REPLACEMENT_CHARS['\n'] = "\\n".toCharArray(); |
REPLACEMENT_CHARS['\r'] = "\\r".toCharArray(); |
REPLACEMENT_CHARS['\f'] = "\\f".toCharArray(); |
HTML_SAFE_REPLACEMENT_CHARS = REPLACEMENT_CHARS.clone(); |
HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027".toCharArray(); |
HTML_SAFE_REPLACEMENT_CHARS['<'] = "\\u003c".toCharArray(); |
HTML_SAFE_REPLACEMENT_CHARS['>'] = "\\u003e".toCharArray(); |
HTML_SAFE_REPLACEMENT_CHARS['&'] = "\\u0026".toCharArray(); |
HTML_SAFE_REPLACEMENT_CHARS['='] = "\\u003d".toCharArray(); |
} |
private final static char[] _INT_TO_CHARARRAY = new char[10]; |
static { |
for (int i = 0; i < 10; i++) { |
_INT_TO_CHARARRAY[i] = (char) (i + 48); |
} |
} |
private final static char[] NULL_VALUE = {'n', 'u', 'l', 'l'}; |
private final static char[] TRUE_VALUE = {'t', 'r', 'u', 'e'}; |
private final static char[] FALSE_VALUE = {'f', 'a', 'l', 's', 'e'}; |
// seems to work well, but maybe a smaller value would be better? |
private final static int _LIMIT_WRITE_TO_BUFFER = 64; |
private final boolean htmlSafe; |
private final boolean skipNull; |
private final Writer writer; |
final Deque<JsonType> _ctx = new ArrayDeque<JsonType>(10); |
private boolean _hasPrevious; |
private char[] _name; |
private final boolean indentation; |
private final static char[] _indentation = new char[]{' ', ' '}; |
private final char[] _buffer = new char[1024]; |
private final int _bufferSize = _buffer.length; |
private int _len = 0; |
List<MetadataPair> _metadata = new ArrayList<MetadataPair>(); |
private class MetadataPair { |
final String name; |
final String value; |
public MetadataPair(String name, String value) { |
super(); |
this.name = name; |
this.value = value; |
} |
} |
public JsonWriter(Writer writer) { |
this(writer, false, false, false); |
} |
public JsonWriter(Writer writer, final boolean skipNull, final boolean htmlSafe, |
boolean indentation) { |
this.writer = writer; |
this.skipNull = skipNull; |
this.htmlSafe = htmlSafe; |
this.indentation = indentation; |
_ctx.push(JsonType.EMPTY); |
} |
public JsonType enclosingType() { |
return _ctx.peek(); |
} |
public void close() { |
flush(); |
try { |
writer.close(); |
} catch (IOException e) { |
throw new JsonStreamException(e); |
} |
} |
public void flush() { |
flushBuffer(); |
try { |
writer.flush(); |
} catch (IOException e) { |
throw new JsonStreamException(e); |
} |
} |
public JsonWriter beginArray() { |
clearMetadata(); |
if (_ctx.peek() == JsonType.OBJECT && _name == null) |
throw new JsonStreamException( |
"Englobing scope is OBJECT before begining a new value call writeName."); |
return begin(JsonType.ARRAY, '['); |
} |
public JsonWriter beginObject() { |
if (_ctx.peek() == JsonType.METADATA) { |
_ctx.pop(); |
begin(JsonType.OBJECT, '{'); |
for (MetadataPair pair : _metadata) { |
writeName('@' + pair.name); |
beforeValue(); |
writeInternalString(pair.value); |
} |
} else begin(JsonType.OBJECT, '{'); |
return this; |
} |
protected final JsonWriter begin(final JsonType jsonType, final char token) { |
beforeValue(); |
_ctx.push(jsonType); |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = token; |
_hasPrevious = false; |
return this; |
} |
public JsonWriter endArray() { |
return end(JsonType.ARRAY, ']'); |
} |
public JsonWriter endObject() { |
return end(JsonType.OBJECT, '}'); |
} |
private final JsonWriter end(final JsonType jsonType, final char token) { |
JsonType jt = _ctx.pop(); |
if (jt != jsonType) |
throw new JsonStreamException("Expect type " + jsonType.name() + " but was written " |
+ jt.name() + ", you must call the adequate beginXXX method before endXXX."); |
if (indentation) { |
_buffer[_len++] = '\n'; |
for (int i = 0; i < _ctx.size() - 1; i++) |
writeToBuffer(_indentation, 0, 2); |
} |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = token; |
_hasPrevious = true; |
return this; |
} |
private final JsonWriter beforeValue() { |
final JsonType enclosingType = _ctx.peek(); |
if (enclosingType == JsonType.ARRAY) { |
if (_name != null) throw newIllegalKeyValuePairInJsonArray(new String(_name)); |
if (_hasPrevious) { |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = ','; |
} |
indent(); |
} else if (_name != null) { |
final int l = _name.length; |
// hum I dont think there may be names with a length near to 1024... we flush only once |
if ((_len + 4 + l) >= _bufferSize) flushBuffer(); |
if (_hasPrevious) _buffer[_len++] = ','; |
indent(); |
if ((_len + 3 + l) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = '"'; |
writeToBuffer(_name, 0, l); |
_buffer[_len++] = '"'; |
_buffer[_len++] = ':'; |
_name = null; |
} else if (enclosingType == JsonType.OBJECT) throw newIllegalSingleValueInJsonObject(); |
return this; |
} |
private JsonStreamException newIllegalKeyValuePairInJsonArray(String name) { |
return JsonStreamException |
.niceTrace(new JsonStreamException( |
"Tried to write key/value pair with key=" |
+ name |
+ ", Json format does not allow key/value pairs inside arrays, only allowed for Json Objects.")); |
} |
private JsonStreamException newIllegalSingleValueInJsonObject() { |
return JsonStreamException.niceTrace(new JsonStreamException( |
"Tried to write value with no key in a JsonObject, Json format does not allow " |
+ "values without keys in JsonObjects, authorized only for arrays.")); |
} |
private final void clearMetadata() { |
if (_ctx.peek() == JsonType.METADATA) { |
_metadata.clear(); |
_ctx.pop(); |
} |
} |
protected void indent() { |
if (indentation) { |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
if (_ctx.peek() != JsonType.EMPTY) _buffer[_len++] = '\n'; |
int len = _ctx.peek() == JsonType.METADATA ? _ctx.size() - 2 : _ctx.size() - 1; |
for (int i = 0; i < len; i++) |
writeToBuffer(_indentation, 0, 2); |
} |
} |
public JsonWriter writeName(final String name) { |
_name = escapeString(name); |
return this; |
} |
public ObjectWriter writeEscapedName(char[] name) { |
_name = name; |
return this; |
} |
public JsonWriter writeValue(int value) { |
clearMetadata(); |
beforeValue(); |
// ok so the buffer must always be bigger than the max length of a long |
if ((_len + 11) >= _bufferSize) flushBuffer(); |
if (value < 0) { |
_buffer[_len++] = '-'; |
writeInt(-((long) value)); |
} else writeInt(value); |
_hasPrevious = true; |
return this; |
} |
public JsonWriter writeValue(final double value) { |
checkValidJsonDouble(value); |
clearMetadata(); |
beforeValue(); |
writeToBuffer(Double.toString(value), 0); |
_hasPrevious = true; |
return this; |
} |
public JsonWriter writeValue(long value) { |
clearMetadata(); |
beforeValue(); |
// ok so the buffer must always be bigger than the max length of a long |
if ((_len + 21) >= _bufferSize) flushBuffer(); |
if (value < 0) { |
if (value != Long.MIN_VALUE) { |
_buffer[_len++] = '-'; |
writeInt(-1 * value); |
} else writeToBuffer(Long.toString(value), 0); |
} else writeInt(value); |
_hasPrevious = true; |
return this; |
} |
public ObjectWriter writeValue(short value) { |
clearMetadata(); |
beforeValue(); |
// ok so the buffer must always be bigger than the max length of a short |
if ((_len + 5) >= _bufferSize) flushBuffer(); |
if (value < 0) { |
_buffer[_len++] = '-'; |
value *= -1; |
} |
writeInt(value); |
_hasPrevious = true; |
return this; |
} |
public ObjectWriter writeValue(float value) { |
checkValidJsonFloat(value); |
clearMetadata(); |
beforeValue(); |
writeToBuffer(Float.toString(value), 0); |
_hasPrevious = true; |
return this; |
} |
public JsonWriter writeValue(final boolean value) { |
clearMetadata(); |
beforeValue(); |
if (value) writeToBuffer(TRUE_VALUE, 0, 4); |
else writeToBuffer(FALSE_VALUE, 0, 5); |
_hasPrevious = true; |
return this; |
} |
protected final int writeInt(long value) { |
final int len = (int) Math.log10(value) + 1; |
if (value == 0) { |
_buffer[_len++] = '0'; |
return 1; |
} |
int pos = _len + len - 1; |
long intPart; |
for (; value > 0; ) { |
intPart = value / 10; |
_buffer[pos--] = _INT_TO_CHARARRAY[(int) (value - (intPart * 10))]; |
value = intPart; |
} |
_len += len; |
return len; |
} |
public JsonWriter writeValue(final Number value) { |
checkValidJsonDouble(value); |
checkValidJsonFloat(value); |
clearMetadata(); |
beforeValue(); |
writeToBuffer(value.toString(), 0); |
_hasPrevious = true; |
return this; |
} |
public ObjectWriter writeBoolean(final Boolean value) { |
if (value == null) return writeNull(); |
else return writeValue(value); |
} |
public ObjectWriter writeNumber(final Number value) { |
if (value == null) return writeNull(); |
else return writeValue(value); |
} |
public ObjectWriter writeString(String value) { |
if (value == null) return writeNull(); |
else return writeValue(value); |
} |
public ObjectWriter writeBytes(byte[] value) { |
if (value == null) return writeNull(); |
else return writeValue(value); |
} |
private void checkValidJsonDouble(Number num) { |
if (num.equals(Double.NaN)) |
throw new NumberFormatException("NaN is not a valid json number."); |
if (num.equals(Double.NEGATIVE_INFINITY) || num.equals(Double.POSITIVE_INFINITY)) |
throw new NumberFormatException("Infinity is not a valid json number."); |
} |
private void checkValidJsonFloat(Number num) { |
if (num.equals(Float.NaN)) |
throw new NumberFormatException("NaN is not a valid json number."); |
if (num.equals(Float.NEGATIVE_INFINITY) || num.equals(Float.POSITIVE_INFINITY)) |
throw new NumberFormatException("Infinity is not a valid json number."); |
} |
public ObjectWriter writeValue(byte[] value) { |
clearMetadata(); |
beforeValue(); |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = '"'; |
final char[] charArray = Base64.encodeToChar(value, false); |
writeToBuffer(charArray, 0, charArray.length); |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = '"'; |
_hasPrevious = true; |
flush(); |
return this; |
} |
public JsonWriter writeUnsafeValue(final String value) { |
clearMetadata(); |
beforeValue(); |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = '"'; |
writeToBuffer(value.toCharArray(), 0, value.length()); |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = '"'; |
_hasPrevious = true; |
return this; |
} |
public JsonWriter writeValue(final String value) { |
clearMetadata(); |
beforeValue(); |
writeInternalString(value); |
return this; |
} |
public final static char[] escapeString(final String value) { |
StringBuilder sb = new StringBuilder(); |
int last = 0; |
final int length = value.length(); |
for (int i = 0; i < length; i++) { |
char c = value.charAt(i); |
char[] replacement; |
if (c < 128) { |
replacement = REPLACEMENT_CHARS[c]; |
if (replacement == null) { |
continue; |
} |
} else if (c == '\u2028') { |
replacement = "\\u2028".toCharArray(); |
} else if (c == '\u2029') { |
replacement = "\\u2029".toCharArray(); |
} else { |
continue; |
} |
if (last < i) { |
sb.append(value, last, i - last); |
} |
sb.append(replacement, 0, replacement.length); |
last = i + 1; |
} |
if (last < length) { |
sb.append(value, last, length); |
} |
return sb.toString().toCharArray(); |
} |
private final void writeInternalString(final String value) { |
final char[][] replacements = htmlSafe ? HTML_SAFE_REPLACEMENT_CHARS : REPLACEMENT_CHARS; |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = '"'; |
int last = 0; |
final int length = value.length(); |
final char[] carray = value.toCharArray(); |
for (int i = 0; i < length; i++) { |
char c = carray[i]; |
char[] replacement; |
if (c < 128) { |
replacement = replacements[c]; |
if (replacement == null) { |
continue; |
} |
} else if (c == '\u2028') { |
replacement = "\\u2028".toCharArray(); |
} else if (c == '\u2029') { |
replacement = "\\u2029".toCharArray(); |
} else { |
continue; |
} |
if (last < i) { |
writeToBuffer(carray, last, i - last); |
} |
writeToBuffer(replacement, 0, replacement.length); |
last = i + 1; |
} |
if (last < length) { |
writeToBuffer(carray, last, length - last); |
} |
if ((_len + 1) >= _bufferSize) flushBuffer(); |
_buffer[_len++] = '"'; |
_hasPrevious = true; |
} |
public ObjectWriter writeNull() { |
if (skipNull) { |
_name = null; |
} else { |
beforeValue(); |
writeToBuffer(NULL_VALUE, 0, 4); |
_hasPrevious = true; |
} |
return this; |
} |
public ObjectWriter beginNextObjectMetadata() { |
// this way we can use this method multiple times in different converters before calling beginObject |
if (_ctx.peek() != JsonType.METADATA) { |
_ctx.push(JsonType.METADATA); |
_metadata.clear(); |
} |
return this; |
} |
public ObjectWriter writeMetadata(String name, String value) { |
if (_ctx.peek() == JsonType.METADATA) _metadata.add(new MetadataPair(name, value)); |
else if (_ctx.peek() == JsonType.OBJECT) { |
writeName('@' + name); |
writeValue(value); |
} |
// else do nothing so we silently don't write metadata for literals and arrays |
return this; |
} |
public ObjectWriter writeBoolean(String name, Boolean value) { |
writeName(name); |
return writeBoolean(value); |
} |
public ObjectWriter writeNumber(String name, Number value) { |
writeName(name); |
return writeNumber(value); |
} |
public ObjectWriter writeString(String name, String value) { |
writeName(name); |
return writeString(value); |
} |
public ObjectWriter writeBytes(String name, byte[] value) { |
writeName(name); |
return writeBytes(value); |
} |
private final void writeToBuffer(final char[] data, final int offset, final int length) { |
if (length < _LIMIT_WRITE_TO_BUFFER && length < (_bufferSize - _len)) { |
System.arraycopy(data, offset, _buffer, _len, length); |
_len += length; |
} else { |
flushBuffer(); |
try { |
writer.write(data, offset, length); |
} catch (IOException e) { |
throw new JsonStreamException(e); |
} |
} |
} |
private final void writeToBuffer(final String data, final int offset) { |
writeToBuffer(data, offset, data.length()); |
} |
private final void writeToBuffer(final String data, final int offset, final int length) { |
if (length < _LIMIT_WRITE_TO_BUFFER && length < (_bufferSize - _len)) { |
data.getChars(offset, offset + length, _buffer, _len); |
_len += length; |
} else { |
flushBuffer(); |
try { |
writer.write(data, offset, length); |
} catch (IOException e) { |
throw new JsonStreamException(e); |
} |
} |
} |
private final void flushBuffer() { |
try { |
if (_len > 0) { |
writer.write(_buffer, 0, _len); |
_len = 0; |
} |
} catch (IOException ioe) { |
throw new JsonStreamException(ioe); |
} |
} |
public Writer unwrap() { |
return writer; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/stream/ObjectReader.java |
---|
New file |
0,0 → 1,236 |
package com.owlike.genson.stream; |
import java.io.Closeable; |
import java.io.IOException; |
/** |
* ObjectReader is part of the streaming api, it's implementations allow you to read data from the |
* stream. The root of the input should always be a object, an array or a literal. There may be some |
* differences between implementations if they try to be compliant with their format specification. |
* <p/> |
* <ul> |
* <li>To read an array call {@link #beginArray()} then use {@link #hasNext()} to check if there is |
* a next element and then call {@link #next()} to advance. When you call next in an array it will |
* read the next value and return its type {@link ValueType}. Use it to check values type and to |
* retrieve its value (if it is a literal) use one of valueAsXXX methods, otherwise it is an array |
* or object. When hasNext returns false terminate the array by calling {@link #endArray()}. |
* <li>To read a object call {@link #beginObject()} then use {@link #hasNext()} to check if there is |
* a next property and then call {@link #next()} to read the name/value pair and {@link #name()} to |
* retrieve its name. If the value is a literal retrieve its value with valueAsXXX methods, |
* otherwise it is an array or object. When you finished reading all properties call |
* {@link #endObject()}. |
* <li>Objects can also contain metadata as their first properties. To read object metadata you have |
* two options: |
* <ol> |
* <li>Just begin your object with beginObject and then retrieve the metadata that you want with |
* {@link #metadata(String) metadata(nameOfTheMetadataProperty)}. |
* <li>Use {@link #nextObjectMetadata()} to read the next objects metadata without calling |
* beginObject. This is useful when you want to handle some metadata in a converter and then |
* delegate the rest to another converter (that will call beginObject or again nextObjectMetadata, |
* so for him it will be transparent that you retrieved already some metadata and he will still be |
* able to retrieve the same data). |
* </ol> |
* <li>To read a literal use valueAsXXX methods. Actual implementation allows literals as root and |
* is relatively tolerant to wrong types, for example if the stream contains the string "123" but |
* you want to retrieve it as a int, {@link JsonReader#valueAsInt()} will parse it and return 123. |
* It does also conversion between numeric types (double <-> int etc). |
* <li>To skip a value use {@link #skipValue()}. If the value is an object or an array it will skip |
* all its content. |
* </ul> |
* <p/> |
* Here is an example if you want to use directly the streaming api instead of the databind api (or |
* if you write a custom converter or deserializer). |
* <p/> |
* <pre> |
* public static void main(String[] args) { |
* // we will read from json to Person |
* Person.read(new JsonReader("{\"name\":\"eugen\",\"age\":26, \"childrenYearOfBirth\":[]}")); |
* } |
* |
* class Person { |
* String name; |
* int age; |
* List<Integer> childrenYearOfBirth; |
* |
* public static Person read(ObjectReader reader) { |
* Person p = new Person(); |
* for (; reader.hasNext();) { |
* if ("name".equals(reader.name())) |
* p.name = reader.valueAsString(); |
* else if ("age".equals(reader.name())) |
* p.age = reader.valueAsInt(); |
* else if ("childrenYearOfBirth".equals(reader.name())) { |
* if (reader.getValueType() == TypeValue.NULL) |
* p.childrenYearOfBirth = null; |
* else { |
* reader.beginArray(); |
* p.childrenYearOfBirth = new ArrayList<Integer>(); |
* for (int i = 0; reader.hasNext(); i++) |
* p.childrenYearOfBirth.add(reader.valueAsInt()); |
* reader.endArray(); |
* } |
* } |
* } |
* return p; |
* } |
* } |
* </pre> |
* |
* @author eugen |
* @see ValueType |
* @see JsonReader |
* @see ObjectWriter |
* @see JsonWriter |
*/ |
public interface ObjectReader extends Closeable { |
/** |
* Starts reading a object. Objects contain name/value pairs. Call {@link #endObject()} when the |
* objects contains no more properties. |
* |
* @return a reference to the reader. |
* @throws JsonStreamException |
*/ |
ObjectReader beginObject(); |
/** |
* Ends the object. If you were not in an object or the object contains more data, an exception |
* will be thrown. |
* |
* @return a reference to the reader. |
* @throws JsonStreamException |
*/ |
ObjectReader endObject(); |
/** |
* Starts reading an array. Arrays contain only values. Call {@link #endArray()} when the array |
* contains no more values. |
* |
* @return a reference to the reader. |
* @throws JsonStreamException |
*/ |
ObjectReader beginArray(); |
/** |
* Ends the array. If you were not in an array or the array contains more data, an exception |
* will be thrown. |
* |
* @return a reference to the reader. |
* @throws JsonStreamException |
*/ |
ObjectReader endArray(); |
/** |
* Will read nexts object metadata. You can call this method as many times as you want, with the |
* condition that you use only {@link #metadata(String)} method. For example if you call |
* {@link #beginObject()} you wont be able to do it anymore (however you still can retrieve the |
* metadata!). |
* |
* @return a reference to the reader. |
* @throws JsonStreamException |
*/ |
ObjectReader nextObjectMetadata(); |
/** |
* If we are in a object it will read the next name/value pair and if we are in an array it will |
* read the next value (except if value is of complex type, in that case after the call to |
* next() you must use one of beginXXX methods). |
* |
* @return the type of the value, see {@link ValueType} for possible types. |
* @throws JsonStreamException |
*/ |
ValueType next(); |
/** |
* @return true if there is a next property or value, false otherwise. |
* @throws JsonStreamException |
*/ |
boolean hasNext(); |
/** |
* If the value is of complex type it will skip its content. |
* |
* @return a reference to the reader. |
* @throws JsonStreamException |
*/ |
ObjectReader skipValue(); |
/** |
* @return The type of current value. |
* @see ValueType |
*/ |
ValueType getValueType(); |
/** |
* @param name the name of the metadata to retrieve. |
* @return value of metadata with name as key or null if there is no such metadata. |
* @throws JsonStreamException |
*/ |
String metadata(String name); |
/** |
* @return the name of current property, valid only if we are in a object and you called |
* {@link #next()} before. |
* @throws JsonStreamException |
*/ |
String name(); |
/** |
* @return the current value as a String. It will try to convert the actual value to String if |
* its not of that type. |
* @throws JsonStreamException |
*/ |
String valueAsString(); |
/** |
* @throws JsonStreamException |
* @throws NumberFormatException |
* @see #valueAsString() |
*/ |
int valueAsInt(); |
/** |
* @throws JsonStreamException |
* @throws NumberFormatException |
* @see #valueAsString() |
*/ |
long valueAsLong(); |
/** |
* @throws JsonStreamException |
* @throws NumberFormatException |
* @see #valueAsString() |
*/ |
double valueAsDouble(); |
/** |
* @throws JsonStreamException |
* @throws NumberFormatException |
* @see #valueAsString() |
*/ |
short valueAsShort(); |
/** |
* @throws JsonStreamException |
* @throws NumberFormatException |
* @see #valueAsString() |
*/ |
float valueAsFloat(); |
/** |
* @throws JsonStreamException |
* @see #valueAsString() |
*/ |
boolean valueAsBoolean(); |
/** |
* @return the incoming base64 string converted to a byte array. |
* @throws JsonStreamException |
*/ |
byte[] valueAsByteArray(); |
JsonType enclosingType(); |
int column(); |
int row(); |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/stream/JsonType.java |
---|
New file |
0,0 → 1,8 |
package com.owlike.genson.stream; |
public enum JsonType { |
EMPTY, |
OBJECT, |
ARRAY, |
METADATA |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/stream/JsonReader.java |
---|
New file |
0,0 → 1,972 |
package com.owlike.genson.stream; |
import java.io.IOException; |
import java.io.Reader; |
import java.io.StringReader; |
import java.util.ArrayDeque; |
import java.util.Arrays; |
import java.util.Deque; |
import java.util.HashMap; |
import java.util.Map; |
import static com.owlike.genson.stream.ValueType.*; |
public class JsonReader implements ObjectReader { |
protected final static int[] SKIPPED_TOKENS; |
static { |
SKIPPED_TOKENS = new int[128]; |
SKIPPED_TOKENS['\t'] = 1; |
SKIPPED_TOKENS['\b'] = 1; |
SKIPPED_TOKENS['\n'] = 1; |
SKIPPED_TOKENS['\r'] = 1; |
SKIPPED_TOKENS['\f'] = 1; |
SKIPPED_TOKENS[' '] = 1; |
} |
private final static boolean[] _NEXT_TOKEN = new boolean[128]; |
static { |
_NEXT_TOKEN[','] = true; |
_NEXT_TOKEN['"'] = true; |
_NEXT_TOKEN['{'] = true; |
_NEXT_TOKEN['['] = true; |
_NEXT_TOKEN['n'] = true; |
_NEXT_TOKEN['N'] = true; |
_NEXT_TOKEN['-'] = true; |
_NEXT_TOKEN['t'] = true; |
_NEXT_TOKEN['f'] = true; |
_NEXT_TOKEN['T'] = true; |
_NEXT_TOKEN['F'] = true; |
for (int i = 48; i < 58; i++) |
_NEXT_TOKEN[i] = true; |
} |
private final static char[] _END_OF_LINE = new char[]{'\n'}; |
private final static char[] _END_OF_BLOCK_COMMENT = new char[]{'*', '/'}; |
/* |
* Recupere dans Jackson |
*/ |
private final static int[] sHexValues = new int[128]; |
static { |
Arrays.fill(sHexValues, -1); |
for (int i = 0; i < 10; ++i) { |
sHexValues['0' + i] = i; |
} |
for (int i = 0; i < 6; ++i) { |
sHexValues['a' + i] = 10 + i; |
sHexValues['A' + i] = 10 + i; |
} |
} |
private final static double[] _POWS = new double[309]; |
static { |
for (int i = 0; i < _POWS.length; i++) |
_POWS[i] = Math.pow(10, i); |
} |
private final Reader reader; |
private final boolean strictDoubleParse; |
private final boolean readMetadata; |
private final char[] _buffer = new char[2048]; |
private int _col; |
private int _row; |
private int _cursor; |
private int _buflen; |
private char[] _stringBuffer = new char[16]; |
private int _stringBufferTail = 0; |
private int _stringBufferLength = _stringBuffer.length; |
private String currentName; |
private String _stringValue; |
protected long _intValue; |
protected double _doubleValue; |
private int _numberLen = 0; |
private Boolean _booleanValue; |
private ValueType valueType; |
private boolean _first = true; |
private boolean _metadata_readen = false; |
private Map<String, String> _metadata = new HashMap<String, String>(5); |
private final Deque<JsonType> _ctx = new ArrayDeque<JsonType>(10); |
{ |
_ctx.push(JsonType.EMPTY); |
} |
public JsonReader(String source) { |
this(new StringReader(source), false, false); |
} |
public JsonReader(Reader reader, boolean strictDoubleParse, boolean readMetadata) { |
this.reader = reader; |
this.strictDoubleParse = strictDoubleParse; |
this.readMetadata = readMetadata; |
char token = (char) readNextToken(false); |
if ('[' == token) valueType = ARRAY; |
else if ('{' == token) valueType = OBJECT; |
else { |
// ok lets try to read next |
if (_buflen > 0) { |
try { |
valueType = consumeValue(); |
} catch (JsonStreamException jse) { |
try { |
// we must cheat because consumeString attends the current token to be " |
// and will increment the cursor |
_cursor = -1; |
_col = -1; |
_stringValue = consumeString('"'); |
valueType = STRING; |
} catch (RuntimeException re) { |
throw re; |
} |
} |
if (valueOf(valueType.name()) == null) |
throw new JsonStreamException( |
"Failed to instanciate reader, first character was " + (char) token |
+ " when possible characters are [ and {"); |
} else valueType = NULL; |
} |
} |
public void close() { |
try { |
reader.close(); |
} catch (IOException e) { |
throw new JsonStreamException(e); |
} |
} |
public ObjectReader beginArray() { |
begin('[', JsonType.ARRAY); |
valueType = ARRAY; |
if (_metadata_readen) _metadata.clear(); |
return this; |
} |
public ObjectReader beginObject() { |
if (!_metadata_readen) { |
begin('{', JsonType.OBJECT); |
valueType = OBJECT; |
if (readMetadata) { |
_metadata.clear(); |
readMetadata(); |
} |
} |
return this; |
} |
public ObjectReader nextObjectMetadata() { |
return beginObject(); |
} |
public ObjectReader endArray() { |
end(']', JsonType.ARRAY); |
return this; |
} |
public ObjectReader endObject() { |
end('}', JsonType.OBJECT); |
_metadata.clear(); |
_metadata_readen = false; |
return this; |
} |
public String name() { |
if (enclosingType() != JsonType.OBJECT) |
throw new JsonStreamException("Only json objects have names, actual type is " |
+ valueType); |
return currentName; |
} |
public String valueAsString() { |
if (STRING == valueType) return _stringValue; |
if (INTEGER == valueType) return "" + _intValue; |
if (DOUBLE == valueType) return "" + _doubleValue; |
if (NULL == valueType) return null; |
if (BOOLEAN == valueType) { |
return _booleanValue.toString(); |
} |
throw new JsonStreamException("Readen value can not be converted to String"); |
} |
public int valueAsInt() { |
if (INTEGER == valueType) { |
int value = (int) _intValue; |
if (value != _intValue) throwNumberFormatException("an int", "overflowing long value " + _intValue); |
return value; |
} else if (DOUBLE == valueType) { |
int value = (int) _doubleValue; |
long longValue = (long) _doubleValue; |
// lets accept only if the integer part is the same and ignore the decimals |
if (value != longValue) { |
throwNumberFormatException("an int", "overflowing double value " + _doubleValue); |
} |
return value; |
} else if (STRING == valueType) return Integer.parseInt(_stringValue); |
throw new JsonStreamException("Expected a int but value is of type " + valueType); |
} |
public long valueAsLong() { |
if (INTEGER == valueType) { |
return _intValue; |
} else if (DOUBLE == valueType) { |
if (Long.MIN_VALUE > _doubleValue || _doubleValue > Long.MAX_VALUE) { |
throwNumberFormatException("a long", "overflowing double value " + _doubleValue); |
} |
return (long) _doubleValue; |
} else if (STRING == valueType) return Long.parseLong(_stringValue); |
throw new JsonStreamException("Expected a long but value is of type " + valueType); |
} |
public double valueAsDouble() { |
if (DOUBLE == valueType) { |
return _doubleValue; |
} else if (INTEGER == valueType) { |
// for the moment lets do that even if there is some precision loss... |
return Long.valueOf(_intValue).doubleValue(); |
} else if (STRING == valueType) return Double.parseDouble(_stringValue); |
throw new JsonStreamException("Expected a double but value is of type " + valueType); |
} |
public short valueAsShort() { |
if (INTEGER == valueType) { |
short value = (short) _intValue; |
if (value != _intValue) throwNumberFormatException("a short", "overflowing long value " + _intValue); |
return value; |
} else if (DOUBLE == valueType) { |
short value = (short) _doubleValue; |
long longValue = (long) _doubleValue; |
// lets accept only if the integer part is the same and ignore the decimals |
if (value != longValue) { |
throwNumberFormatException("a short", "overflowing double value " + _doubleValue); |
} |
return value; |
} else if (STRING == valueType) return Short.parseShort(_stringValue); |
throw new JsonStreamException("Expected a short but value is of type " + valueType); |
} |
public float valueAsFloat() { |
if (DOUBLE == valueType) { |
return (float) _doubleValue; |
} else if (INTEGER == valueType) { |
// same as for doubles, for the moment lets do that even if there is some precision |
// loss... |
return Long.valueOf(_intValue).floatValue(); |
} else if (STRING == valueType) return Float.parseFloat(_stringValue); |
throw new JsonStreamException("Expected a float but value is of type " + valueType); |
} |
public boolean valueAsBoolean() { |
if (BOOLEAN == valueType) { |
return _booleanValue; |
} |
if (STRING == valueType) return Boolean.parseBoolean(_stringValue); |
throw new JsonStreamException("Readen value is not of type boolean"); |
} |
public byte[] valueAsByteArray() { |
if (STRING == valueType) return Base64.decodeFast(_stringValue); |
if (NULL == valueType) return null; |
throw new JsonStreamException("Expected a String to convert to byte array found " |
+ valueType); |
} |
public String metadata(String name) { |
if (!_metadata_readen) nextObjectMetadata(); |
return _metadata.get(name); |
} |
public ValueType getValueType() { |
return valueType; |
} |
public ObjectReader skipValue() { |
if (ARRAY == valueType || OBJECT == valueType) { |
int balance = 0; |
do { |
if (ARRAY == valueType) { |
beginArray(); |
balance++; |
} else if (OBJECT == valueType) { |
beginObject(); |
balance++; |
} |
while (hasNext()) { |
next(); |
skipValue(); |
} |
JsonType type = _ctx.peek(); |
if (JsonType.ARRAY == type) { |
endArray(); |
balance--; |
} else if (JsonType.OBJECT == type) { |
endObject(); |
balance--; |
} |
} while (balance > 0); |
} |
return this; |
} |
public boolean hasNext() { |
int token = readNextToken(false); |
if (token == -1) return false; |
if (token < 128) { |
if (_first || _ctx.size() == 1) return _NEXT_TOKEN[token]; |
else if (token == ',') return true; |
} |
return false; |
} |
public ValueType next() { |
_metadata_readen = false; |
_first = false; |
char ctoken = (char) readNextToken(false); |
if (ctoken == ',') { |
_cursor++; |
ctoken = (char) readNextToken(false); |
} else if (JsonType.ARRAY == _ctx.peek()) { |
if (ctoken == '[') { |
valueType = ARRAY; |
return valueType; |
} |
if (ctoken == '{') { |
valueType = OBJECT; |
return valueType; |
} |
} |
if (JsonType.OBJECT == _ctx.peek()) { |
currentName = consumeString(ctoken); |
if (readNextToken(true) != ':') newWrongTokenException(":", _cursor - 1); |
} |
valueType = consumeValue(); |
return valueType; |
} |
@Override |
public JsonType enclosingType() { |
return _ctx.peek(); |
} |
protected final ValueType consumeValue() { |
char ctoken = (char) readNextToken(false); |
if (ctoken == '"') { |
_stringValue = consumeString(ctoken); |
return STRING; |
} else if (ctoken == '[') return ARRAY; |
else if (ctoken == '{') return OBJECT; |
else return consumeLiteral(); |
} |
protected final void readMetadata() { |
_metadata_readen = true; |
while (true) { |
char ctoken = (char) readNextToken(false); |
if ('"' != ctoken) return; |
ensureBufferHas(2, true); |
if ('@' == _buffer[_cursor + 1]) { |
_cursor++; |
// we cheat here... |
String key = consumeString(ctoken); |
if (readNextToken(true) != ':') newWrongTokenException(":", _cursor - 1); |
String value = consumeString((char) readNextToken(false)); |
_metadata.put(key, value); |
if (readNextToken(false) == ',') { |
_cursor++; |
} |
} else return; |
} |
} |
protected final void begin(int character, JsonType type) { |
int token = readNextToken(true); |
// seems unecessary as it is done in readNextToken |
// checkIllegalEnd(token); |
if (character == token) { |
_ctx.push(type); |
} else newWrongTokenException("" + (char) character, _cursor - 1); |
_first = true; |
} |
protected final void end(int character, JsonType type) { |
int token = readNextToken(true); |
// seems unecessary as it is done in readNextToken |
// checkIllegalEnd(token); |
if (character == token && type == _ctx.peek()) { |
_ctx.pop(); |
} else newWrongTokenException("" + (char) character, _cursor - 1); |
_first = false; |
} |
protected final String consumeString(int token) { |
if (token != '"') newMisplacedTokenException(_cursor); |
_cursor++; |
boolean buffered = false; |
while (true) { |
if (fillBuffer(true) < 0) { |
// TODO ugly to copy, by the way ensure we don't have the same problem elsewhere |
String name = new String(_stringBuffer, 0, _stringBufferTail); |
_stringBufferTail = 0; |
return name; |
} |
int i = _cursor; |
for (; i < _buflen; ) { |
if (_buffer[i] == '"') { |
if (buffered) { |
writeToStringBuffer(_buffer, _cursor, i - _cursor); |
_cursor = i + 1; |
String name = new String(_stringBuffer, 0, _stringBufferTail); |
_stringBufferTail = 0; |
return name; |
} else { |
String name = new String(_buffer, _cursor, i - _cursor); |
_cursor = i + 1; |
return name; |
} |
} else if (_buffer[i] == '\\') { |
buffered = true; |
writeToStringBuffer(_buffer, _cursor, i - _cursor); |
_cursor = i + 1; |
if (_stringBufferLength <= (_stringBufferTail + 1)) expandStringBuffer(16); |
_stringBuffer[_stringBufferTail++] = readEscaped(); |
i = _cursor; |
} else i++; |
} |
buffered = true; |
writeToStringBuffer(_buffer, _cursor, i - _cursor); |
_cursor = i + 1; |
} |
} |
/** |
* Reads the next literal value into _booleanValue, _doubleValue or _intValue and returns the |
* type of the readed literal, possible values are : INTEGER, DOUBLE, BOOLEAN, NULL. When |
* calling this method the _cursor must be positioned on the first character of the value in the |
* _buffer, you can ensure that by calling {@link #readNextToken(boolean)}. |
*/ |
protected final ValueType consumeLiteral() { |
int token = _buffer[_cursor]; |
if ((token > 47 && token < 58) || token == 45) { |
return consumeNumber(); |
} else { |
ensureBufferHas(4, true); |
if ((_buffer[_cursor] == 'N' || _buffer[_cursor] == 'n') |
&& (_buffer[_cursor + 1] == 'U' || _buffer[_cursor + 1] == 'u') |
&& (_buffer[_cursor + 2] == 'L' || _buffer[_cursor + 2] == 'l') |
&& (_buffer[_cursor + 3] == 'L' || _buffer[_cursor + 3] == 'l')) { |
_cursor += 4; |
return NULL; |
} |
if ((_buffer[_cursor] == 'T' || _buffer[_cursor] == 't') |
&& (_buffer[_cursor + 1] == 'R' || _buffer[_cursor + 1] == 'r') |
&& (_buffer[_cursor + 2] == 'U' || _buffer[_cursor + 2] == 'u') |
&& (_buffer[_cursor + 3] == 'E' || _buffer[_cursor + 3] == 'e')) { |
_booleanValue = true; |
_cursor += 4; |
return BOOLEAN; |
} |
ensureBufferHas(5, true); |
if ((_buffer[_cursor] == 'F' || _buffer[_cursor] == 'f') |
&& (_buffer[_cursor + 1] == 'A' || _buffer[_cursor + 1] == 'a') |
&& (_buffer[_cursor + 2] == 'L' || _buffer[_cursor + 2] == 'l') |
&& (_buffer[_cursor + 3] == 'S' || _buffer[_cursor + 3] == 's') |
&& (_buffer[_cursor + 4] == 'E' || _buffer[_cursor + 4] == 'e')) { |
_booleanValue = false; |
_cursor += 5; |
return BOOLEAN; |
} else { |
throw new JsonStreamException.Builder().message( |
"Illegal character around row " + _row + " and column " + (_cursor - _col) |
+ " awaited for literal (number, boolean or null) but read '" |
+ _buffer[_cursor] + "'!").create(); |
} |
} |
} |
private ValueType consumeNumber() { |
// lets fill the buffer and handle differently overflowing values |
if ((_buflen - _cursor) < 378) ensureBufferHas(_buflen, false); |
int begin = _cursor; |
int cur; |
boolean negative; |
// check the sign |
if (_buffer[_cursor] == 45) { |
negative = true; |
_cursor++; |
cur = _cursor; |
} else { |
negative = false; |
cur = _cursor; |
} |
// just to handle invalid leading 0000 |
for (; cur < _buflen && _buffer[cur] == 48; cur++) ; |
// Careful we consume the '-' here, but also all the leading 0, even if it is of form 0.xxx |
_cursor = cur; |
int len = Math.min(_buflen, cur + 18); |
int token; |
long longValue = 0; |
for (; cur < len; cur++) { |
token = _buffer[cur]; |
if (token < 48 || token > 57) { |
break; |
} |
longValue = 10L * longValue + (token - 48); |
} |
if (cur < _buflen) { |
// read the maximum we can to fill the long capacity, at max we can read 1 additional |
// digit |
token = _buffer[cur]; |
if (token > 47 && token < 58) { |
long newLongValue = 10L * longValue + (token - 48); |
if (newLongValue > longValue) { |
longValue = newLongValue; |
cur++; |
} |
/* TODO we parse here Long.MIN_VALUE as a double, we had also pb in the writer |
* the solution => instead of doing operations on positive numbers, do the inverse, use negative numbers |
* as the min negative long holds the - max positive long. |
*/ |
// else we exceed long capacity, just continue and parse it as a double |
} |
if (cur < _buflen |
&& ((token = _buffer[cur]) == 46 || token == 101 || token == 69 || (token > 47 && token < 58))) { |
if (strictDoubleParse) { |
_cursor = begin; |
return consumeStrictNumber(cur); |
} else return consumeDouble(cur, longValue, negative); |
} |
} |
_intValue = negative ? -longValue : longValue; |
_numberLen = cur - _cursor; |
_cursor = cur; |
return INTEGER; |
} |
// a custom implementation based on fast path principles |
private ValueType consumeDouble(int cur, long longValue, boolean negative) { |
int token; |
// TODO a verifier : on ne veut compter dans les intDigit que ce qui n'est pas pris dans le |
// longValue, idem pour les decimales?? |
int intDigits = cur - _cursor; |
int valueDigits = longValue > 0 ? cur - _cursor : 0; |
// ok we have readen as many characters as a long can contain |
// the next readen characters will serve for the digit count for large integer numbers |
if (intDigits > 17) { |
for (; cur < _buflen; cur++) { |
if (_buffer[cur] < 48 || _buffer[cur] > 57) { |
break; |
} |
} |
// only if we advanced |
if (intDigits != (cur - _cursor)) intDigits = (cur - _cursor) - intDigits; |
} else |
// TODO check ça m'a l'air bizarre comme cas... |
intDigits = 0; |
int decimalDigits = 0; |
// next possible case is a dot |
if (cur < _buflen && _buffer[cur] == 46) { |
cur++; |
int start = cur; |
// if integer part value is zero we could use scientific notation and win in precision |
if (longValue == 0) { |
// reset the counter as we don't care of the leading zeros |
intDigits = 0; |
// now lets try to read as many consecutive zeros as available |
for (; cur < _buflen && _buffer[cur] == 48; cur++) ; |
} |
// ok now we must read again into the longValue |
int len = Math.min(_buflen, cur + (18 - valueDigits)); |
for (; cur < len; cur++) { |
token = _buffer[cur]; |
if (token < 48 || token > 57) { |
break; |
} |
longValue = 10L * longValue + (token - 48); |
} |
decimalDigits = cur - start; |
start = cur; |
// no need to count digits after the precision we support for decimals |
// continue reading digits and just ignore the values, we will truncate |
for (; cur < _buflen; cur++) { |
if (_buffer[cur] < 48 || _buffer[cur] > 57) { |
break; |
} |
} |
} |
// now try to read exponent E/e |
if ((cur + 1) < _buflen && (_buffer[cur] == 101 || _buffer[cur] == 69)) { |
token = _buffer[++cur]; |
boolean negativeExp; |
// check the sign |
if (token == 45) { |
negativeExp = true; |
cur++; |
} else { |
if (token == 43) cur++; |
negativeExp = false; |
} |
// read the power of ten |
int powValue = 0; |
for (; cur < _buflen; cur++) { |
token = _buffer[cur]; |
if (token < 48 || token > 57) { |
break; |
} |
powValue = 10 * powValue + (token - 48); |
} |
// depending on the sign put it in the decimal digit counter or integer digit counter |
if (negativeExp) decimalDigits += powValue; |
else intDigits += powValue; |
} |
// and now make the difference so it balances well |
decimalDigits = intDigits - decimalDigits; |
if (decimalDigits < 0) { |
/* |
* This allows to handle also Double.MIN_VALUE and Double.MIN_NORMAL as |
* Double.MIN_NORMAL has 16 decimal digits, 308+16=324, but 10^324 overflows the double |
* capacity, its Infinity. So we use this little trick. |
*/ |
if (decimalDigits < -308) { |
if (decimalDigits < -325) { |
_doubleValue = 0; |
} else { |
_doubleValue = longValue / _POWS[-decimalDigits - 308]; |
_doubleValue = _doubleValue / _POWS[308]; |
} |
} else { |
// better precision than multiplication |
_doubleValue = longValue / _POWS[-decimalDigits]; |
} |
} else { |
if (decimalDigits > 308) { |
_doubleValue = Double.POSITIVE_INFINITY; |
} else { |
// we don't need to apply same technique as before as |
// 308-16=292 so it's okay :) |
_doubleValue = longValue * _POWS[decimalDigits]; |
} |
} |
_doubleValue = negative ? -_doubleValue : _doubleValue; |
_numberLen = cur - _cursor; |
_cursor = cur; |
return DOUBLE; |
} |
private final ValueType consumeStrictNumber(int localCursor) { |
if (localCursor < _buflen) { |
// consider all the remaining integer values as part of the double |
for (; localCursor < _buflen; localCursor++) { |
if (_buffer[localCursor] < 48 || _buffer[localCursor] > 57) { |
break; |
} |
} |
} |
if (localCursor < _buflen) { |
if (_buffer[localCursor] == '.') { |
localCursor = advanceWhileNumeric(++localCursor); |
} |
} |
if (localCursor + 1 < _buflen) { |
char ctoken = _buffer[localCursor]; |
if (ctoken == 'e' || ctoken == 'E') { |
ctoken = _buffer[++localCursor]; |
if (ctoken == '-' || ctoken == '+' || (ctoken > 47 && ctoken < 58)) { |
localCursor = advanceWhileNumeric(++localCursor); |
} else newWrongTokenException("'-' or '+' or '' (same as +)"); |
} |
} |
// it may overflow of the max numeric value we accept to read, it should |
if (localCursor >= _buflen) { |
} |
_numberLen = localCursor - _cursor; |
_stringValue = new String(_buffer, _cursor, _numberLen); |
_doubleValue = Double.parseDouble(_stringValue); |
_cursor = localCursor; |
return DOUBLE; |
} |
private int advanceWhileNumeric(int cursor) { |
for (; cursor < _buflen; cursor++) { |
if ((_buffer[cursor] < 48 || _buffer[cursor] > 57)) { |
return cursor; |
} |
} |
return cursor; |
} |
protected final int readNextToken(boolean consume) { |
while (true) { |
if (_cursor >= _buflen) fillBuffer(true); |
for (; _cursor < _buflen; _cursor++) { |
int token = _buffer[_cursor]; |
if (token < 128 && SKIPPED_TOKENS[token] == 0) { |
if (token == '/') { |
ensureBufferHas(2, true); |
if (_buffer[_cursor + 1] == '*') { |
_cursor += 2; |
advanceAfter(_END_OF_BLOCK_COMMENT); |
} else if (_buffer[_cursor + 1] == '/') { |
_cursor += 2; |
advanceAfter(_END_OF_LINE); |
_row++; |
_col = _cursor; |
} else newWrongTokenException("start comment // or /*", _cursor); |
// don't consume the token |
_cursor--; |
} else if (consume) { |
return _buffer[_cursor++]; |
} else return token; |
} else if (_buffer[_cursor] == '\n') { |
_row++; |
_col = _cursor; |
} |
} |
if (_buflen == -1) break; |
} |
return _cursor < _buflen ? _buffer[_cursor] : -1; |
} |
private final void advanceAfter(char[] str) { |
int strPos = 0; |
while (true) { |
if (_cursor >= _buflen) fillBuffer(true); |
for (; _cursor < _buflen && strPos < str.length; _cursor++) { |
if (_buffer[_cursor] == str[strPos]) { |
strPos++; |
} else strPos = 0; |
} |
if (strPos == str.length) { |
return; |
} |
if (_buflen == -1) break; |
} |
} |
protected final char readEscaped() { |
fillBuffer(true); |
char token = _buffer[_cursor++]; |
switch (token) { |
case 'b': |
return '\b'; |
case 't': |
return '\t'; |
case 'n': |
return '\n'; |
case 'f': |
return '\f'; |
case 'r': |
return '\r'; |
case '"': |
case '/': |
case '\\': |
return token; |
case 'u': |
break; |
default: |
newMisplacedTokenException(_cursor - 1); |
} |
int value = 0; |
if (ensureBufferHas(4, false) < 0) { |
throw new JsonStreamException("Expected 4 hex-digit for character escape sequence!"); |
// System.arraycopy(buffer, cursor, buffer, 0, buflen-cursor); |
// buflen = buflen - cursor + reader.read(buffer, buflen-cursor, cursor); |
// cursor = 0; |
} |
for (int i = 0; i < 4; ++i) { |
int ch = _buffer[_cursor++]; |
int digit = (ch > 127) ? -1 : sHexValues[ch]; |
if (digit < 0) { |
throw new JsonStreamException("Wrong character '" + ch |
+ "' expected a hex-digit for character escape sequence!"); |
} |
value = (value << 4) | digit; |
} |
return (char) value; |
} |
private final void writeToStringBuffer(final char[] data, final int offset, final int length) { |
if (_stringBufferLength <= (_stringBufferTail + length)) { |
expandStringBuffer(length); |
} |
System.arraycopy(data, offset, _stringBuffer, _stringBufferTail, length); |
_stringBufferTail += length; |
} |
private final void expandStringBuffer(int length) { |
char[] extendedStringBuffer = new char[_stringBufferLength * 2 + length]; |
System.arraycopy(_stringBuffer, 0, extendedStringBuffer, 0, _stringBufferTail); |
_stringBuffer = extendedStringBuffer; |
_stringBufferLength = extendedStringBuffer.length; |
} |
private final int fillBuffer(boolean doThrow) { |
if (_cursor < _buflen) return _buflen; |
try { |
_buflen = reader.read(_buffer); |
} catch (IOException ioe) { |
throw new JsonStreamException(ioe); |
} |
checkIllegalEnd(_buflen); |
_cursor = 0; |
_col = 0; |
return _buflen; |
} |
private final int ensureBufferHas(int minLength, boolean doThrow) { |
try { |
int actualLen = _buflen - _cursor; |
if (actualLen >= minLength) { |
return actualLen; |
} |
System.arraycopy(_buffer, _cursor, _buffer, 0, actualLen); |
for (; actualLen < minLength; ) { |
int len = reader.read(_buffer, actualLen, _buffer.length - actualLen); |
if (len < 0) { |
if (doThrow) throw new JsonStreamException( |
"Encountered end of stream, incomplete json!"); |
else { |
_buflen = actualLen; |
_col = 0; |
_cursor = 0; |
return len; |
} |
} |
actualLen += len; |
} |
_buflen = actualLen; |
_col = 0; |
_cursor = 0; |
return actualLen; |
} catch (IOException ioe) { |
throw new JsonStreamException(ioe); |
} |
} |
protected final boolean isEOF() { |
return _buflen < 0 || fillBuffer(false) < 0; |
} |
private final void newWrongTokenException(String awaited) { |
newWrongTokenException(awaited, _cursor); |
} |
public int column() { |
int col = _cursor - _col; |
return col < 0 ? 0 : col; |
} |
public int row() { |
return _row; |
} |
private final void newWrongTokenException(String awaited, int cursor) { |
// otherwise it fails when an error occurs on first character |
if (cursor < 0) cursor = 0; |
int pos = cursor - _col; |
if (pos < 0) pos = 0; |
if (_buflen < 0) throw new JsonStreamException( |
"Incomplete data or malformed json : encoutered end of stream but expected " |
+ awaited).niceTrace(); |
else throw new JsonStreamException.Builder() |
.message( |
"Illegal character at row " + _row + " and column " + pos + " expected " |
+ awaited + " but read '" + _buffer[cursor] + "' !") |
.locate(_row, pos).create().niceTrace(); |
} |
private final void newMisplacedTokenException(int cursor) { |
if (_buflen < 0) |
throw JsonStreamException.niceTrace(new JsonStreamException( |
"Incomplete data or malformed json : encoutered end of stream.")); |
if (cursor < 0) cursor = 0; |
int pos = cursor - _col; |
if (pos < 0) pos = 0; |
throw new JsonStreamException.Builder() |
.message( |
"Encountred misplaced character '" + _buffer[cursor] + "' around row " |
+ _row + " and column " + pos).locate(_row, pos).create().niceTrace(); |
} |
private final void checkIllegalEnd(int token) { |
if (token == -1 && JsonType.EMPTY != _ctx.peek()) |
throw new JsonStreamException( |
"Incomplete data or malformed json : encoutered end of stream!").niceTrace(); |
} |
private void throwNumberFormatException(String expected, String encoutered) { |
int pos = _cursor - _col - _numberLen; |
throw JsonStreamException.niceTrace(new NumberFormatException("Wrong numeric type at row " + _row + " and column " + pos |
+ ", expected " + expected + " but encoutered " + encoutered)); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/stream/ValueType.java |
---|
New file |
0,0 → 1,24 |
package com.owlike.genson.stream; |
import java.util.List; |
import java.util.Map; |
public enum ValueType { |
ARRAY(List.class), |
OBJECT(Map.class), |
STRING(String.class), |
INTEGER(Long.class), |
DOUBLE(Double.class), |
BOOLEAN(Boolean.class), |
NULL(null); |
private final Class<?> clazz; |
ValueType(Class<?> clazz) { |
this.clazz = clazz; |
} |
public Class<?> toClass() { |
return clazz; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/GenericType.java |
---|
New file |
0,0 → 1,70 |
package com.owlike.genson; |
import java.lang.reflect.ParameterizedType; |
import java.lang.reflect.Type; |
import com.owlike.genson.reflect.TypeUtil; |
/** |
* This class is a holder for generic types so we can work around type erasure. You can read <a |
* href="http://gafter.blogspot.fr/2006/12/super-type-tokens.html">this blog post</a> who explains a |
* bit more in details what it is about. For example if you want to use at runtime a |
* List<Integer> : |
* <p/> |
* <pre> |
* GenericType<List<Integer>> genericType = new GenericType<List<Integer>>() { |
* }; |
* List<Integer> listOfIntegers = new Genson().deserialize("[1,2,3]", genericType); |
* |
* // if you want to get the standard java.lang.reflect.Type corresponding to List<Integer> from |
* // genericType |
* Type listOfIntegersType = genericType.getType(); |
* // listOfIntegersType will be an instance of ParameterizedType with Integer class as type argument |
* </pre> |
* |
* @param <T> the real type |
* @author eugen |
*/ |
public abstract class GenericType<T> { |
private final Type type; |
private final Class<T> rawClass; |
@SuppressWarnings("unchecked") |
protected GenericType() { |
Type superType = getClass().getGenericSuperclass(); |
if (superType instanceof Class<?>) { |
throw new IllegalArgumentException("You must specify the parametrized type!"); |
} |
type = ((ParameterizedType) superType).getActualTypeArguments()[0]; |
rawClass = (Class<T>) TypeUtil.getRawClass(type); |
} |
private GenericType(Class<T> rawClass) { |
this.type = rawClass; |
this.rawClass = rawClass; |
} |
@SuppressWarnings("unchecked") |
private GenericType(Type type) { |
this.type = type; |
this.rawClass = (Class<T>) TypeUtil.getRawClass(type); |
} |
public static <T> GenericType<T> of(Class<T> rawClass) { |
return new GenericType<T>(rawClass) { |
}; |
} |
public static GenericType<Object> of(Type type) { |
return new GenericType<Object>(type) { |
}; |
} |
public Type getType() { |
return type; |
} |
public Class<T> getRawClass() { |
return rawClass; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/ext/GensonBundle.java |
---|
New file |
0,0 → 1,45 |
package com.owlike.genson.ext; |
import com.owlike.genson.GensonBuilder; |
import com.owlike.genson.reflect.AbstractBeanDescriptorProvider.ContextualConverterFactory; |
import com.owlike.genson.reflect.BeanDescriptorProvider; |
import com.owlike.genson.reflect.BeanMutatorAccessorResolver; |
import com.owlike.genson.reflect.BeanPropertyFactory; |
import com.owlike.genson.reflect.PropertyNameResolver; |
/** |
* Bundles allow to package all kind of Genson customizations into a single module and register |
* them all together. Extensions are registered using Genson.Builder. |
* <p/> |
* <pre> |
* Genson genson = new GensonBuilder().with(new SuperCoolExtension()).create(); |
* </pre> |
* <p/> |
* Extension configuration is mixed with user custom configuration (no way to distinguish them), |
* however user custom config. has preference over bundle configuration. This means that you can |
* override bundle configuration with custom one. |
* <p/> |
* |
* <b>Important note, bundles must be registered after any other configuration.</b> |
* |
* This part of the API is still in beta, it could change in the future in order to make it more |
* powerful. |
* |
* @author eugen |
*/ |
public abstract class GensonBundle { |
/** |
* This method does not provide any guarantee to when it is called: before user config, during, |
* or after. Thus it should not rely on accessor methods from GensonBuilder they might not reflect |
* the final configuration. Use the builder to register your components. |
*/ |
public abstract void configure(GensonBuilder builder); |
public BeanDescriptorProvider createBeanDescriptorProvider(ContextualConverterFactory contextualConverterFactory, |
BeanPropertyFactory propertyFactory, |
BeanMutatorAccessorResolver propertyResolver, |
PropertyNameResolver nameResolver, |
GensonBuilder builder) { |
return null; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/ext/guava/GuavaBundle.java |
---|
New file |
0,0 → 1,13 |
package com.owlike.genson.ext.guava; |
import com.google.common.base.Optional; |
import com.owlike.genson.GensonBuilder; |
import com.owlike.genson.ext.GensonBundle; |
public class GuavaBundle extends GensonBundle { |
@Override |
public void configure(GensonBuilder builder) { |
builder.useDefaultValue(Optional.absent(), Optional.class) |
.withConverterFactory(new OptionalConverter.OptionalConverterFactory()); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/ext/guava/OptionalConverter.java |
---|
New file |
0,0 → 1,46 |
package com.owlike.genson.ext.guava; |
import com.google.common.base.Optional; |
import com.owlike.genson.*; |
import com.owlike.genson.annotation.HandleNull; |
import com.owlike.genson.reflect.TypeUtil; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
import com.owlike.genson.stream.ValueType; |
import java.io.IOException; |
import java.lang.reflect.Type; |
@HandleNull |
public class OptionalConverter<T> implements Converter<Optional<T>> { |
static class OptionalConverterFactory implements Factory<Converter<Optional<Object>>> { |
@Override |
public Converter<Optional<Object>> create(Type type, Genson genson) { |
Type typeOfValue = TypeUtil.typeOf(0, type); |
return new OptionalConverter<Object>(genson.provideConverter(typeOfValue)); |
} |
} |
private final Converter<T> valueConverter; |
public OptionalConverter(Converter<T> valueConverter) { |
this.valueConverter = valueConverter; |
} |
@Override |
public void serialize(Optional<T> object, ObjectWriter writer, Context ctx) throws Exception { |
if (object == null || object.isPresent()) { |
valueConverter.serialize(object.get(), writer, ctx); |
} else writer.writeNull(); |
} |
@Override |
public Optional<T> deserialize(ObjectReader reader, Context ctx) throws Exception { |
if (ValueType.NULL.equals(reader.getValueType())) { |
return Optional.absent(); |
} |
return Optional.of(valueConverter.deserialize(reader, ctx)); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/ext/jaxb/JAXBBundle.java |
---|
New file |
0,0 → 1,536 |
package com.owlike.genson.ext.jaxb; |
import static com.owlike.genson.reflect.TypeUtil.*; |
import java.lang.annotation.Annotation; |
import java.lang.reflect.AccessibleObject; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.Field; |
import java.lang.reflect.Member; |
import java.lang.reflect.Method; |
import java.lang.reflect.Type; |
import java.text.ParseException; |
import java.text.SimpleDateFormat; |
import java.util.Arrays; |
import java.util.GregorianCalendar; |
import java.util.HashMap; |
import java.util.Map; |
import javax.xml.bind.annotation.*; |
import javax.xml.bind.annotation.adapters.XmlAdapter; |
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; |
import javax.xml.datatype.DatatypeConfigurationException; |
import javax.xml.datatype.DatatypeFactory; |
import javax.xml.datatype.Duration; |
import javax.xml.datatype.XMLGregorianCalendar; |
import com.owlike.genson.*; |
import com.owlike.genson.annotation.HandleClassMetadata; |
import com.owlike.genson.annotation.HandleBeanView; |
import com.owlike.genson.convert.ChainedFactory; |
import com.owlike.genson.convert.ContextualFactory; |
import com.owlike.genson.convert.DefaultConverters.WrappedRootValueConverter; |
import com.owlike.genson.convert.DefaultConverters.DateConverter; |
import com.owlike.genson.ext.GensonBundle; |
import com.owlike.genson.reflect.*; |
import com.owlike.genson.stream.ObjectReader; |
import com.owlike.genson.stream.ObjectWriter; |
/** |
* Provides support for some JAXB annotations and data types. |
* |
* @author eugen |
*/ |
public class JAXBBundle extends GensonBundle { |
private final DatatypeFactory dateFactory; |
private boolean wrapRootValues = false; |
public JAXBBundle() { |
try { |
dateFactory = DatatypeFactory.newInstance(); |
} catch (DatatypeConfigurationException dce) { |
throw new IllegalStateException("Could not obtain an instance of DatatypeFactory.", dce); |
} |
} |
/** |
* When enabled allows to use @XmlRootElement annotation on root objects to wrap them inside a object under some key. |
* The key by default will be the class name with the first letter to lower case. |
*/ |
public JAXBBundle wrapRootValues(boolean enable) { |
wrapRootValues = enable; |
return this; |
} |
@Override |
public void configure(GensonBuilder builder) { |
// forcing here the order of GensonAnnotationsResolver and AnnotationPropertyNameResolver allows |
// us to give them preference over Jaxb annotations. We can not assume it true for any bundle, |
// as in some cases a bundle might want to have preference over all std Genson components |
builder.withConverters(new XMLGregorianCalendarConverter(), new DurationConveter()) |
.with(new BeanMutatorAccessorResolver.GensonAnnotationsResolver(), new JaxbAnnotationsResolver()) |
.with(new PropertyNameResolver.AnnotationPropertyNameResolver(), new JaxbNameResolver()) |
.withConverterFactory(new EnumConverterFactory()) |
.withBeanPropertyFactory(new JaxbBeanPropertyFactory()) |
.withContextualFactory(new XmlTypeAdapterFactory()); |
if (wrapRootValues) |
builder.withConverterFactory(new ChainedFactory() { |
@Override |
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) { |
Class<?> clazz = TypeUtil.getRawClass(type); |
XmlRootElement ann = clazz.getAnnotation(XmlRootElement.class); |
if (ann != null) { |
String name = "##default".equals(ann.name()) ? firstCharToLower(clazz.getSimpleName()) : ann.name(); |
return new WrappedRootValueConverter<Object>(name, name, (Converter<Object>) nextConverter); |
} |
return null; |
} |
}); |
} |
private String firstCharToLower(String str) { |
return Character.toLowerCase(str.charAt(0)) + (str.length() > 0 ? str.substring(1) : ""); |
} |
private class DurationConveter implements Converter<Duration> { |
@Override |
public void serialize(Duration object, ObjectWriter writer, Context ctx) { |
writer.writeValue(object.toString()); |
} |
@Override |
public Duration deserialize(ObjectReader reader, Context ctx) { |
return dateFactory.newDuration(reader.valueAsString()); |
} |
} |
@HandleClassMetadata |
@HandleBeanView |
private class XMLGregorianCalendarConverter implements Converter<XMLGregorianCalendar> { |
private final DateConverter converter = new DateConverter(); |
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-DD'T'hh:mm:ssZ"); |
@Override |
public void serialize(XMLGregorianCalendar object, ObjectWriter writer, Context ctx) { |
converter.serialize(object.toGregorianCalendar().getTime(), writer, ctx); |
} |
@Override |
public synchronized XMLGregorianCalendar deserialize(ObjectReader reader, Context ctx) { |
GregorianCalendar cal = new GregorianCalendar(); |
try { |
cal.setTime(dateFormat.parse(reader.valueAsString())); |
} catch (ParseException e) { |
throw new JsonBindingException("Could not parse date " |
+ reader.valueAsString(), e); |
} |
return dateFactory.newXMLGregorianCalendar(cal); |
} |
} |
private class XmlTypeAdapterFactory implements ContextualFactory<Object> { |
@Override |
public Converter<Object> create(BeanProperty property, Genson genson) { |
XmlJavaTypeAdapter ann = property.getAnnotation(XmlJavaTypeAdapter.class); |
Converter<Object> converter = null; |
if (ann != null) { |
Class<? extends XmlAdapter> adapterClass = ann.value(); |
Type adapterExpandedType = expandType( |
lookupGenericType(XmlAdapter.class, adapterClass), adapterClass); |
Type adaptedType = typeOf(0, adapterExpandedType); |
Type originalType = typeOf(1, adapterExpandedType); |
Type propertyType = property.getType(); |
if (getRawClass(propertyType).isPrimitive()) |
propertyType = wrap(getRawClass(propertyType)); |
XmlElement el = property.getAnnotation(XmlElement.class); |
Type xmlElementType = el != null && el.type() != XmlElement.DEFAULT.class ? el.type() : null; |
// checking type consistency |
if (xmlElementType != null) { |
if (!match(adaptedType, xmlElementType, false)) |
throw new ClassCastException("The BoundType of XMLAdapter " + adapterClass |
+ " is not assignable from property " + property.getName() |
+ " declared in " + property.getDeclaringClass()); |
} else if (!match(propertyType, originalType, false)) |
throw new ClassCastException("The BoundType of XMLAdapter " + adapterClass |
+ " is not assignable from property " + property.getName() |
+ " declared in " + property.getDeclaringClass()); |
try { |
XmlAdapter adapter = adapterClass.newInstance(); |
// we also need to find a converter for the adapted type |
Converter<Object> adaptedTypeConverter = genson.provideConverter( |
xmlElementType != null ? xmlElementType : adaptedType); |
converter = new AdaptedConverter(adapter, adaptedTypeConverter); |
} catch (InstantiationException e) { |
throw new JsonBindingException( |
"Could not instantiate XmlAdapter of type " + adapterClass, e); |
} catch (IllegalAccessException e) { |
throw new JsonBindingException( |
"Could not instantiate XmlAdapter of type " + adapterClass, e); |
} |
} |
return converter; |
} |
private class AdaptedConverter implements Converter<Object> { |
private final XmlAdapter<Object, Object> adapter; |
private final Converter<Object> converter; |
public AdaptedConverter(XmlAdapter<Object, Object> adapter, Converter<Object> converter) { |
super(); |
this.adapter = adapter; |
this.converter = converter; |
} |
@Override |
public Object deserialize(ObjectReader reader, Context ctx) throws Exception { |
Object value = converter.deserialize(reader, ctx); |
try { |
return adapter.unmarshal(value); |
} catch (Exception e) { |
throw new JsonBindingException("Could not unmarshal object using adapter " |
+ adapter.getClass()); |
} |
} |
@Override |
public void serialize(Object object, ObjectWriter writer, Context ctx) throws Exception { |
Object adaptedValue = null; |
try { |
adaptedValue = adapter.marshal(object); |
} catch (Exception e) { |
throw new JsonBindingException("Could not marshal object using adapter " |
+ adapter.getClass()); |
} |
converter.serialize(adaptedValue, writer, ctx); |
} |
} |
} |
private class EnumConverterFactory implements Factory<Converter<Enum<?>>> { |
@Override |
public Converter<Enum<?>> create(Type type, Genson genson) { |
Class<?> rawClass = getRawClass(type); |
if (rawClass.isEnum() || Enum.class.isAssignableFrom(rawClass)) { |
@SuppressWarnings({"unchecked"}) |
Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) rawClass; |
try { |
Map<String, Enum<?>> valueToEnum = new HashMap<String, Enum<?>>(); |
Map<Enum<?>, String> enumToValue = new HashMap<Enum<?>, String>(); |
for (Enum<?> enumConstant : enumClass.getEnumConstants()) { |
XmlEnumValue ann = rawClass.getField(enumConstant.name()).getAnnotation( |
XmlEnumValue.class); |
if (ann != null) { |
valueToEnum.put(ann.value(), enumConstant); |
enumToValue.put(enumConstant, ann.value()); |
} else { |
valueToEnum.put(enumConstant.name(), enumConstant); |
enumToValue.put(enumConstant, enumConstant.name()); |
} |
} |
return new EnumConverter(valueToEnum, enumToValue); |
} catch (SecurityException e) { |
throw new JsonBindingException("Unable to introspect enum " |
+ enumClass, e); |
} catch (NoSuchFieldException e) { |
} |
} |
// otherwise let genson standard converter handle the conversion |
return null; |
} |
@HandleClassMetadata |
@HandleBeanView |
private class EnumConverter implements Converter<Enum<?>> { |
private final Map<String, Enum<?>> valueToEnum; |
private final Map<Enum<?>, String> enumToValue; |
public EnumConverter(Map<String, Enum<?>> valueToEnum, Map<Enum<?>, String> enumToValue) { |
super(); |
this.valueToEnum = valueToEnum; |
this.enumToValue = enumToValue; |
} |
@Override |
public void serialize(Enum<?> object, ObjectWriter writer, Context ctx) { |
writer.writeUnsafeValue(enumToValue.get(object)); |
} |
@Override |
public Enum<?> deserialize(ObjectReader reader, Context ctx) { |
return valueToEnum.get(reader.valueAsString()); |
} |
} |
} |
private class JaxbBeanPropertyFactory implements BeanPropertyFactory { |
@Override |
public PropertyAccessor createAccessor(String name, Field field, Type ofType, Genson genson) { |
Type newType = getType(field, field.getGenericType(), ofType); |
if (newType != null) { |
return new PropertyAccessor.FieldAccessor(name, field, newType, getRawClass(ofType)); |
} |
return null; |
} |
@Override |
public PropertyAccessor createAccessor(String name, Method method, Type ofType, |
Genson genson) { |
Type newType = getType(method, method.getReturnType(), ofType); |
if (newType != null) { |
return new PropertyAccessor.MethodAccessor(name, method, newType, |
getRawClass(ofType)); |
} |
return null; |
} |
@Override |
public PropertyMutator createMutator(String name, Field field, Type ofType, Genson genson) { |
Type newType = getType(field, field.getGenericType(), ofType); |
if (newType != null) { |
return new PropertyMutator.FieldMutator(name, field, newType, getRawClass(ofType)); |
} |
return null; |
} |
@Override |
public PropertyMutator createMutator(String name, Method method, Type ofType, Genson genson) { |
if (method.getParameterTypes().length == 1) { |
Type newType = getType(method, method.getReturnType(), ofType); |
if (newType != null) { |
return new PropertyMutator.MethodMutator(name, method, newType, |
getRawClass(ofType)); |
} |
} |
return null; |
} |
@Override |
public BeanCreator createCreator(Type ofType, Constructor<?> ctr, String[] resolvedNames, |
Genson genson) { |
return null; |
} |
@Override |
public BeanCreator createCreator(Type ofType, Method method, String[] resolvedNames, |
Genson genson) { |
return null; |
} |
private Type getType(AccessibleObject object, Type objectType, Type contextType) { |
XmlElement el = object.getAnnotation(XmlElement.class); |
if (el != null && el.type() != XmlElement.DEFAULT.class) { |
if (!TypeUtil.getRawClass(objectType).isAssignableFrom(el.type())) { |
XmlJavaTypeAdapter ad = object.getAnnotation(XmlJavaTypeAdapter.class); |
if (ad == null) |
throw new ClassCastException("Inavlid XmlElement annotation, " + objectType |
+ " is not assignable from " + el.type()); |
} |
return el.type(); |
} else |
return null; |
} |
} |
private class JaxbNameResolver implements PropertyNameResolver { |
private final static String DEFAULT_NAME = "##default"; |
@Override |
public String resolve(int parameterIdx, Constructor<?> fromConstructor) { |
return null; |
} |
@Override |
public String resolve(int parameterIdx, Method fromMethod) { |
return null; |
} |
@Override |
public String resolve(Field fromField) { |
return extractName(fromField); |
} |
@Override |
public String resolve(Method fromMethod) { |
return extractName(fromMethod); |
} |
private String extractName(AccessibleObject object) { |
String name = null; |
XmlAttribute attr = object.getAnnotation(XmlAttribute.class); |
if (attr != null) |
name = attr.name(); |
else { |
XmlElement el = object.getAnnotation(XmlElement.class); |
if (el != null) name = el.name(); |
} |
return DEFAULT_NAME.equals(name) ? null : name; |
} |
} |
private class JaxbAnnotationsResolver extends BeanMutatorAccessorResolver.PropertyBaseResolver { |
@Override |
public Trilean isAccessor(Field field, Class<?> fromClass) { |
if (ignore(field, field.getType(), fromClass)) return Trilean.FALSE; |
if (include(field, field.getType(), fromClass)) return Trilean.TRUE; |
return shouldResolveField(field, fromClass); |
} |
@Override |
public Trilean isMutator(Field field, Class<?> fromClass) { |
if (ignore(field, field.getType(), fromClass)) return Trilean.FALSE; |
if (include(field, field.getType(), fromClass)) return Trilean.TRUE; |
return shouldResolveField(field, fromClass); |
} |
@Override |
public Trilean isAccessor(Method method, Class<?> fromClass) { |
if (ignore(method, method.getReturnType(), fromClass)) return Trilean.FALSE; |
String name = null; |
if (method.getName().startsWith("get") && method.getName().length() > 3) |
name = method.getName().substring(3); |
else if (method.getName().startsWith("is") && method.getName().length() > 2 |
&& method.getReturnType() == boolean.class |
|| method.getReturnType() == Boolean.class) |
name = method.getName().substring(2); |
if (name != null) { |
if (include(method, method.getReturnType(), fromClass)) return Trilean.TRUE; |
if (find(XmlTransient.class, fromClass, "set" + name, method.getReturnType()) != null) |
return Trilean.FALSE; |
return shouldResolveMethod(method, fromClass); |
} |
return Trilean.FALSE; |
} |
@Override |
public Trilean isMutator(Method method, Class<?> fromClass) { |
Class<?> paramClass = method.getParameterTypes().length == 1 ? method |
.getParameterTypes()[0] : Object.class; |
if (ignore(method, paramClass, fromClass)) return Trilean.FALSE; |
if (method.getName().startsWith("set") && method.getName().length() > 3) { |
if (include(method, method.getReturnType(), fromClass)) return Trilean.TRUE; |
String name = method.getName().substring(3); |
// Exclude it if there is a corresponding accessor annotated with XmlTransient |
if (find(XmlTransient.class, fromClass, "get" + name) != null) return Trilean.FALSE; |
if (paramClass.equals(boolean.class) || paramClass.equals(Boolean.class)) { |
if (find(XmlTransient.class, fromClass, "is" + name) != null) |
return Trilean.FALSE; |
} |
return shouldResolveMethod(method, fromClass); |
} |
return Trilean.FALSE; |
} |
private Trilean shouldResolveField(Field field, Class<?> fromClass) { |
XmlAccessorType ann = find(XmlAccessorType.class, field, fromClass); |
if (isDefaultVisibilityMember(field, ann) || isValidPublicMember(field, ann) || isValidFieldMember(field, ann)) { |
return Trilean.TRUE; |
} else { |
return Trilean.FALSE; |
} |
} |
private Trilean shouldResolveMethod(Method m, Class<?> fromClass) { |
XmlAccessorType ann = find(XmlAccessorType.class, m, fromClass); |
if (isDefaultVisibilityMember(m, ann) || isValidPropertyMember(m, ann) || isValidPublicMember(m, ann)) { |
return Trilean.TRUE; |
} else { |
return Trilean.FALSE; |
} |
} |
private boolean isDefaultVisibilityMember(Member m, XmlAccessorType xmlAccessTypeAnn) { |
return xmlAccessTypeAnn == null && VisibilityFilter.PACKAGE_PUBLIC.isVisible(m); |
} |
private boolean isValidFieldMember(Member m, XmlAccessorType ann) { |
return ann != null && ann.value() == XmlAccessType.FIELD && VisibilityFilter.PRIVATE.isVisible(m); |
} |
private boolean isValidPublicMember(Member m, XmlAccessorType ann) { |
return ann != null && ann.value() == XmlAccessType.PUBLIC_MEMBER && VisibilityFilter.PACKAGE_PUBLIC.isVisible(m); |
} |
private boolean isValidPropertyMember(Member m, XmlAccessorType ann) { |
return ann != null && ann.value() == XmlAccessType.PROPERTY && VisibilityFilter.PACKAGE_PUBLIC.isVisible(m); |
} |
private boolean ignore(AccessibleObject property, Class<?> ofType, Class<?> fromClass) { |
XmlTransient xmlTransientAnn = find(XmlTransient.class, property, ofType); |
if (xmlTransientAnn != null) return true; |
return false; |
} |
private boolean include(AccessibleObject property, Class<?> ofType, Class<?> fromClass) { |
if (find(XmlAttribute.class, property, ofType) != null |
|| find(XmlElement.class, property, ofType) != null) return true; |
return false; |
} |
} |
private <A extends Annotation> A find(Class<A> annotation, AccessibleObject onObject, |
Class<?> onClass) { |
A ann = onObject.getAnnotation(annotation); |
if (ann != null) return ann; |
return find(annotation, onClass); |
} |
private <A extends Annotation> A find(Class<A> annotation, Class<?> onClass) { |
A ann = onClass.getAnnotation(annotation); |
if (ann == null && onClass.getPackage() != null) |
ann = onClass.getPackage().getAnnotation(annotation); |
return ann; |
} |
private <A extends Annotation> A find(Class<A> annotation, Class<?> inClass, String methodName, |
Class<?>... parameterTypes) { |
A ann = null; |
for (Class<?> clazz = inClass; clazz != null; clazz = clazz.getSuperclass()) { |
try { |
for (Method m : clazz.getDeclaredMethods()) |
if (m.getName().equals(methodName) |
&& Arrays.equals(m.getParameterTypes(), parameterTypes)) |
if (m.isAnnotationPresent(annotation)) |
return m.getAnnotation(annotation); |
else |
break; |
} catch (SecurityException e) { |
throw new RuntimeException(e); |
} |
} |
return ann; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/ext/package-info.java |
---|
New file |
0,0 → 1,4 |
/** |
* This package contains features simplifying Genson integration into existing components. |
*/ |
package com.owlike.genson.ext; |
/branches/grupo4/impl/src/java/com/owlike/genson/ThreadLocalHolder.java |
---|
New file |
0,0 → 1,48 |
package com.owlike.genson; |
import static com.owlike.genson.Operations.checkNotNull; |
import java.util.HashMap; |
import java.util.Map; |
/** |
* Just another data holder that stores data in a threadlocal map. |
* If you only want to share data across serializers and deserializers prefer using {@link Context}. |
* Internally Genson uses it for the spring webmvc integration, so it can pass method signatures and |
* extract its annotations, etc. |
* |
* @author eugen |
* @see Context |
* @see com.owlike.genson.ext.spring.ExtendedReqRespBodyMethodProcessor ExtendedReqRespBodyMethodProcessor |
* @see com.owlike.genson.ext.spring.GensonMessageConverter GensonMessageConverter |
*/ |
public final class ThreadLocalHolder { |
private final static ThreadLocal<Map<String, Object>> _data = new ThreadLocal<Map<String, Object>>(); |
public static Object store(String key, Object parameter) { |
checkNotNull(key); |
return getPutIfMissing().put(key, parameter); |
} |
public static <T> T remove(String key, Class<T> valueType) { |
checkNotNull(key, valueType); |
Map<String, Object> map = getPutIfMissing(); |
T value = valueType.cast(map.get(key)); |
map.remove(key); |
return value; |
} |
public static <T> T get(String key, Class<T> valueType) { |
checkNotNull(key, valueType); |
return valueType.cast(getPutIfMissing().get(key)); |
} |
private static Map<String, Object> getPutIfMissing() { |
Map<String, Object> map = _data.get(); |
if (map == null) { |
map = new HashMap<String, Object>(); |
_data.set(map); |
} |
return map; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/Wrapper.java |
---|
New file |
0,0 → 1,102 |
package com.owlike.genson; |
import java.lang.annotation.Annotation; |
import java.lang.reflect.AnnotatedElement; |
import com.owlike.genson.reflect.TypeUtil; |
/** |
* Wrapper class must be extended by decorated converters that wrap other converters. This allows to |
* access merged class information of wrapped converter and the converter itself. So instead of |
* doing myObject.getClass().isAnnotationPresent(..) you will do myObject.isAnnotationPresent(..), |
* where myObject is an instance of Wrapper. For example to check if a converter (or any another |
* encapsulated converter and so on) has annotation @HandleNull you will do it that way: |
* <p/> |
* <pre> |
* Wrapper.toAnnotatedElement(converter).isAnnotationPresent(HandleNull.class); |
* </pre> |
* <p/> |
* In the future there may be other methods to access other kind of class information. |
* |
* @author eugen |
*/ |
public abstract class Wrapper<T> implements AnnotatedElement { |
private AnnotatedElement wrappedElement; |
protected T wrapped; |
protected Wrapper() { |
} |
protected Wrapper(T wrappedObject) { |
if (wrappedObject == null) |
throw new IllegalArgumentException("Null not allowed!"); |
decorate(wrappedObject); |
} |
public Annotation[] getAnnotations() { |
return Operations.union(Annotation[].class, wrappedElement.getAnnotations(), getClass() |
.getAnnotations()); |
} |
public <A extends Annotation> A getAnnotation(Class<A> aClass) { |
A ann = wrappedElement.getAnnotation(aClass); |
return ann == null ? getClass().getAnnotation(aClass) : ann; |
} |
public Annotation[] getDeclaredAnnotations() { |
return Operations.union(Annotation[].class, wrappedElement.getDeclaredAnnotations(), |
getClass().getDeclaredAnnotations()); |
} |
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { |
return wrappedElement.isAnnotationPresent(annotationClass) |
|| getClass().isAnnotationPresent(annotationClass); |
} |
// package visibility as a convenience for CircularClassReferenceConverter |
protected void decorate(T object) { |
if (wrappedElement != null) |
throw new IllegalStateException("An object is already wrapped!"); |
if (object instanceof AnnotatedElement) |
this.wrappedElement = (AnnotatedElement) object; |
else |
this.wrappedElement = object.getClass(); |
this.wrapped = object; |
} |
public T unwrap() { |
return wrapped; |
} |
/** |
* This method acts as an adapter to AnnotatedElement, use it when you need to work on a |
* converter annotations. In fact "object" argument will usually be of type converter. If this |
* class is a wrapper than it will cast it to annotatedElement (as Wrapper implements |
* AnnotatedElement). Otherwise we will return the class of this object. |
* |
* @param object may be an instance of converter for example |
* @return an annotatedElement that allows us to get annotations from this object and it's |
* wrapped classes if it is a Wrapper. |
*/ |
public static AnnotatedElement toAnnotatedElement(Object object) { |
if (object == null) |
return null; |
if (isWrapped(object)) |
return (AnnotatedElement) object; |
else |
return object.getClass(); |
} |
public static boolean isWrapped(Object object) { |
return object instanceof Wrapper; |
} |
/** |
* @return true if this object or its wrapped object (if the object extends Wrapper) is of type clazz. |
*/ |
public static boolean isOfType(Object object, Class<?> clazz) { |
return TypeUtil.match(object.getClass(), clazz, false) || |
(isWrapped(object) && Wrapper.isOfType(((Wrapper<?>) object).unwrap(), clazz)); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/EncodingAwareReaderFactory.java |
---|
New file |
0,0 → 1,108 |
package com.owlike.genson; |
import java.io.*; |
/** |
* This is an internal class that might evolve in the future into a JsonReader Factory and be moved |
* to the stream package. |
*/ |
public final class EncodingAwareReaderFactory { |
static enum UTFEncoding { |
UTF_32BE(4), UTF_32LE(4), UTF_16BE(2), UTF_16LE(2), UTF_8(1), UNKNOWN(-1); |
final int bytes; |
private UTFEncoding(int bytes) { |
this.bytes = bytes; |
} |
public String encoding() { |
return name().replace('_', '-'); |
} |
} |
/** |
* Creates java.io.Reader instances with detected encoding from the input stream |
* using BOM if present or JSON spec. |
* |
* Some links: |
* http://www.herongyang.com/Unicode/ |
* http://www.ietf.org/rfc/rfc4627.txt |
* |
* @throws IOException |
* @throws UnsupportedEncodingException |
*/ |
public Reader createReader(InputStream is) throws IOException { |
byte[] bytes = new byte[4]; |
int len = fetchBytes(bytes, is); |
if (len < 1) return new InputStreamReader(is); |
// read first 4 bytes if available |
int bits_32 = (bytes[0] & 0xFF) << 24 |
| (bytes[1] & 0xFF) << 16 |
| (bytes[2] & 0xFF) << 8 |
| (bytes[3] & 0xFF); |
UTFEncoding encoding = UTFEncoding.UNKNOWN; |
boolean hasBOM = false; |
// try to detect the encoding from those 4 bytes if BOM is used |
if (len == 4) encoding = detectEncodingFromBOM(bits_32); |
// no BOM then fall back to JSON spec |
if (encoding == UTFEncoding.UNKNOWN) { |
encoding = detectEncodingUsingJSONSpec(bits_32); |
} else hasBOM = true; |
// should not happen as we default to UTF-8 |
if (encoding == UTFEncoding.UNKNOWN) { |
throw new UnsupportedEncodingException("The encoding could not be detected from the stream."); |
} |
int usedBOMBytes = hasBOM ? len - (4 - encoding.bytes) : 0; |
int bytesToUnread = len - usedBOMBytes; |
// small optimization to avoid encapsulation when there is nothing to unread |
if (bytesToUnread == 0) { |
return new InputStreamReader(is, encoding.encoding()); |
} else { |
PushbackInputStream pis = new PushbackInputStream(is, bytesToUnread); |
pis.unread(bytes, usedBOMBytes, bytesToUnread); |
return new InputStreamReader(pis, encoding.encoding()); |
} |
} |
private UTFEncoding detectEncodingFromBOM(int bits_32) { |
int bits_16 = bits_32 >>> 16; |
if (bits_32 == 0x0000FEFF) return UTFEncoding.UTF_32BE; |
else if (bits_32 == 0xFFFE0000) return UTFEncoding.UTF_32LE; |
else if (bits_16 == 0xFEFF) return UTFEncoding.UTF_16BE; |
else if (bits_16 == 0xFFFE) return UTFEncoding.UTF_16LE; |
else if (bits_32 >>> 8 == 0xEFBBBF) return UTFEncoding.UTF_8; |
else return UTFEncoding.UNKNOWN; |
} |
private UTFEncoding detectEncodingUsingJSONSpec(int bits_32) { |
int bits_16 = bits_32 >>> 16; |
if (bits_32 >>> 8 == 0) return UTFEncoding.UTF_32BE; |
else if ((bits_32 & 0x00FFFFFF) == 0) return UTFEncoding.UTF_32LE; |
else if ((bits_16 & 0xFF00) == 0) return UTFEncoding.UTF_16BE; |
else if ((bits_16 & 0x00FF) == 0) return UTFEncoding.UTF_16LE; |
else return UTFEncoding.UTF_8; |
} |
private int fetchBytes(byte[] bytes, InputStream is) throws IOException { |
int start = 0; |
int bytesRead; |
while(start < bytes.length-1 && (bytesRead = is.read(bytes, start, bytes.length-start)) > -1) { |
start += bytesRead; |
} |
return start; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/Deserializer.java |
---|
New file |
0,0 → 1,25 |
package com.owlike.genson; |
import java.io.IOException; |
import com.owlike.genson.stream.ObjectReader; |
/** |
* Deserializers handle deserialization by reading data form {@link com.owlike.genson.stream.ObjectReader |
* ObjectReader} and constructing java objects of type T. Genson Deserializers work like classic |
* deserializers from other libraries. |
* |
* @param <T> the type of objects this deserializer can deserialize. |
* @author eugen |
* @see Converter |
*/ |
public interface Deserializer<T> { |
/** |
* @param reader used to read data from. |
* @param ctx the current context. |
* @return an instance of T or a subclass of T. |
* @throws com.owlike.genson.JsonBindingException |
* @throws com.owlike.genson.stream.JsonStreamException |
*/ |
public T deserialize(ObjectReader reader, Context ctx) throws Exception; |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/JsonBindingException.java |
---|
New file |
0,0 → 1,12 |
package com.owlike.genson; |
public class JsonBindingException extends RuntimeException { |
public JsonBindingException(String message) { |
super(message); |
} |
public JsonBindingException(String message, Throwable cause) { |
super(message, cause); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/Operations.java |
---|
New file |
0,0 → 1,36 |
package com.owlike.genson; |
import java.lang.reflect.Array; |
public final class Operations { |
public static <T> T[] union(Class<T[]> tClass, T[]... values) { |
int size = 0; |
for (T[] value : values) |
size += value.length; |
T[] arr = tClass.cast(Array.newInstance(tClass.getComponentType(), size)); |
for (int i = 0, len = 0; i < values.length; len += values[i].length, i++) |
System.arraycopy(values[i], 0, arr, len, values[i].length); |
return arr; |
} |
public static byte[] expandArray(byte[] array, int idx, double factor) { |
if (idx >= array.length) { |
byte[] tmpArray = new byte[(int) (array.length * factor)]; |
System.arraycopy(array, 0, tmpArray, 0, array.length); |
return tmpArray; |
} else return array; |
} |
public static byte[] truncateArray(byte[] array, int size) { |
if (size < array.length) { |
byte[] tmpArray = new byte[size]; |
System.arraycopy(array, 0, tmpArray, 0, size); |
return tmpArray; |
} else return array; |
} |
public static void checkNotNull(Object... values) { |
for (Object value : values) |
if (value == null) throw new IllegalArgumentException("Null not allowed!"); |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/Context.java |
---|
New file |
0,0 → 1,109 |
package com.owlike.genson; |
import java.util.ArrayList; |
import java.util.HashMap; |
import java.util.List; |
import java.util.Map; |
import static com.owlike.genson.Operations.checkNotNull; |
/** |
* The context class is intended to be a statefull class shared across a single execution. Its main |
* purpose is to hold local data and to pass it to others contributors of the |
* serialization/deserialization chain. |
* <p/> |
* For example if you have computed in a first serializer a value and you need it later in another |
* one, you can put it in the context by using {@link Context#store(String, Object)} method and |
* later retrieve it with {@link #get(String, Class)} or remove it with {@link #remove(String, Class)}. |
* <p/> |
* You can achieve the same thing with {@link ThreadLocalHolder} that stores the data in a thread |
* local map but it is cleaner to use this Context class as you wont have to worry about removing |
* values from it. Indeed java web servers reuse created threads, so if you store data in a thread |
* local variable and don't remove it, it will still be present when another request comes in and is |
* bound to that thread! This can also lead to memory leaks! Don't forget the |
* try-store-finally-remove block if you stick with ThreadLocalHolder. |
* <p/> |
* <p/> |
* This class stores also the views present in the current context, those views will be applied to |
* the matching objects during serialization and deserialization. |
* |
* @author eugen |
* @see com.owlike.genson.BeanView BeanView |
* @see com.owlike.genson.convert.BeanViewConverter BeanViewConverter |
* @see com.owlike.genson.ThreadLocalHolder ThreadLocalHolder |
*/ |
public class Context { |
public final Genson genson; |
private List<Class<? extends BeanView<?>>> views; |
private Map<String, Object> _ctxData = new HashMap<String, Object>(); |
public Context(Genson genson) { |
this(genson, null); |
} |
public Context(Genson genson, List<Class<? extends BeanView<?>>> views) { |
checkNotNull(genson); |
this.genson = genson; |
this.views = views; |
} |
public boolean hasViews() { |
return views != null && !views.isEmpty(); |
} |
public Context withView(Class<? extends BeanView<?>> view) { |
if (views == null) views = new ArrayList<Class<? extends BeanView<?>>>(); |
views.add(view); |
return this; |
} |
public List<Class<? extends BeanView<?>>> views() { |
return views; |
} |
/** |
* Puts the object o in the current context indexed by key. |
* |
* @param key must be not null |
* @param o |
* @return the old object associated with that key or null. |
*/ |
public Object store(String key, Object o) { |
checkNotNull(key); |
Object old = _ctxData.get(key); |
_ctxData.put(key, o); |
return old; |
} |
/** |
* Returns the value mapped to key in this context or null. If the value is not of type |
* valueType then an exception is thrown. |
* |
* @param key must be not null |
* @param valueType the type of the value, null not allowed |
* @return the mapping for key or null |
* @throws ClassCastException if the value mapped to key is not of type valueType. |
*/ |
public <T> T get(String key, Class<T> valueType) { |
checkNotNull(key, valueType); |
return valueType.cast(_ctxData.get(key)); |
} |
/** |
* Removes the mapping for this key from the context. If there is no mapping for that key, null |
* is returned. If the value mapped to key is not of type valueType an ClassCastException is |
* thrown and the mapping is not removed. |
* |
* @param key must be not null |
* @param valueType the type of the value, null not allowed |
* @return the value associated to this key |
* @throws ClassCastException if the value mapped to key is not of type valueType. |
* @see com.owlike.genson.Context#get(String, Class) |
*/ |
public <T> T remove(String key, Class<T> valueType) { |
checkNotNull(key, valueType); |
T value = valueType.cast(_ctxData.get(key)); |
_ctxData.remove(key); |
return value; |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/package-info.java |
---|
New file |
0,0 → 1,5 |
/** |
* This package contains Genson base classes, to start |
* with have a look at {@link com.owlike.genson.Genson Genson}. |
*/ |
package com.owlike.genson; |
/branches/grupo4/impl/src/java/com/owlike/genson/Genson.java |
---|
New file |
0,0 → 1,619 |
package com.owlike.genson; |
import java.io.*; |
import java.lang.reflect.Type; |
import java.nio.charset.Charset; |
import java.util.*; |
import java.util.concurrent.ConcurrentHashMap; |
import com.owlike.genson.reflect.BeanDescriptor; |
import com.owlike.genson.reflect.BeanDescriptorProvider; |
import com.owlike.genson.reflect.RuntimePropertyFilter; |
import com.owlike.genson.stream.*; |
/** |
* <p/> |
* Main class of the library. Instances of Genson are thread safe and should be reused. |
* You can instantiate it multiple times but it is better to have an instance per configuration |
* so you can benefit of better performances. |
* <p/> |
* For more examples have a look at the <a |
* href="http://owlike.github.io/genson/">online documentation</a>. |
* <p/> |
* <p/> |
* To create a new instance of Genson you can use the default no arg constructor or the |
* {@link GensonBuilder} class to have control over Gensons configuration. <br> |
* A basic usage of Genson would be: |
* <p/> |
* <pre> |
* Genson genson = new Genson(); |
* String json = genson.serialize(new int[] { 1, 2, 3 }); |
* int[] arrayOfInt = genson.deserialize(json, int[].class); |
* // (multi-dimensional arrays are also supported) |
* |
* // genson can also deserialize primitive types without class information |
* Number[] arrayOfNumber = genson.deserialize("[1, 2.03, 8877463]", Number[].class); |
* // or even |
* Object[] arrayOfUnknownTypes = genson |
* .deserialize("[1, false, null, 4.05, \"hey this is a string!\"]", Object[].class); |
* // it can also deserialize json objects of unknown types to standard java types such as Map, Array, Long etc. |
* Map<String, Object> map = (Map<String, Object>) genson.deserialize("{\"foo\":1232}", Object.class); |
* </pre> |
* <p/> |
* |
* Every object serialized with Genson, can be deserialized back into its concrete type! Just enable |
* it via the builder {@link com.owlike.genson.GensonBuilder#useClassMetadata(boolean)}. |
* |
* You can also deserialize to objects that don't provide a default constructor with no arguments |
* {@link com.owlike.genson.GensonBuilder#useConstructorWithArguments(boolean)}. |
* |
* @author eugen |
*/ |
public final class Genson { |
/** |
* Default genson configuration, the default configuration (sers, desers, etc) will be shared |
* accros all default Genson instances. |
*/ |
private final static Genson _default = new GensonBuilder().create(); |
private final static Charset UTF8_CHARSET = Charset.forName("UTF-8"); |
private final ConcurrentHashMap<Type, Converter<?>> converterCache = new ConcurrentHashMap<Type, Converter<?>>(); |
private final Factory<Converter<?>> converterFactory; |
private final BeanDescriptorProvider beanDescriptorFactory; |
private final Map<Class<?>, String> classAliasMap; |
private final Map<String, Class<?>> aliasClassMap; |
private final boolean skipNull; |
private final boolean htmlSafe; |
private final boolean withClassMetadata; |
private final boolean withMetadata; |
private final boolean strictDoubleParse; |
private final boolean indent; |
private final boolean failOnMissingProperty; |
private final EncodingAwareReaderFactory readerFactory = new EncodingAwareReaderFactory(); |
private final Map<Class<?>, Object> defaultValues; |
private final RuntimePropertyFilter runtimePropertyFilter; |
/** |
* The default constructor will use the default configuration provided by the {@link GensonBuilder}. |
* In most cases using this default constructor will suffice. |
*/ |
public Genson() { |
this(_default.converterFactory, _default.beanDescriptorFactory, |
_default.skipNull, _default.htmlSafe, _default.aliasClassMap, |
_default.withClassMetadata, _default.strictDoubleParse, _default.indent, |
_default.withMetadata, _default.failOnMissingProperty, _default.defaultValues, _default.runtimePropertyFilter); |
} |
/** |
* Instead of using this constructor you should use {@link GensonBuilder}. |
* @param converterFactory providing instance of converters. |
* @param beanDescProvider providing instance of {@link BeanDescriptor |
* BeanDescriptor} used during bean serialization and deserialization. |
* @param skipNull indicates whether null values should be serialized. False by default, null values |
* will be serialized. |
* @param htmlSafe indicates whether \,<,>,&,= characters should be replaced by their Unicode |
* representation. |
* @param classAliases association map between classes and their aliases, used if withClassMetadata is |
* true. |
* @param withClassMetadata indicates whether class name should be serialized and used during deserialization |
* to determine the type. False by default. |
* @param strictDoubleParse indicates whether to use or not double approximation. If false (by default) it |
* enables Genson custom double parsing algorithm, that is an approximation of |
* Double.parse but is a lot faster. If true, Double.parse method will be usead |
* instead. In most cases you should be fine with Genson algorithm, but if for some |
* reason you need to have 100% match with Double.parse, then enable strict parsing. |
* @param indent true if outputed json must be indented (pretty printed). |
* @param withMetadata true if ObjectReader instances must be configured with metadata feature enabled. |
* if withClassMetadata is true withMetadata will be automatically true. |
* @param failOnMissingProperty throw a JsonBindingException when a key in the json stream does not match a property in the Java Class. |
* @param defaultValues contains a mapping from the raw class to the default value that should be used when the property is missing. |
* @param runtimePropertyFilter is used to define what bean properties should be excluded from ser/de at runtime. |
*/ |
public Genson(Factory<Converter<?>> converterFactory, BeanDescriptorProvider beanDescProvider, |
boolean skipNull, boolean htmlSafe, Map<String, Class<?>> classAliases, boolean withClassMetadata, |
boolean strictDoubleParse, boolean indent, boolean withMetadata, boolean failOnMissingProperty, |
Map<Class<?>, Object> defaultValues, RuntimePropertyFilter runtimePropertyFilter) { |
this.converterFactory = converterFactory; |
this.beanDescriptorFactory = beanDescProvider; |
this.skipNull = skipNull; |
this.htmlSafe = htmlSafe; |
this.aliasClassMap = classAliases; |
this.withClassMetadata = withClassMetadata; |
this.defaultValues = defaultValues; |
this.runtimePropertyFilter = runtimePropertyFilter; |
this.classAliasMap = new HashMap<Class<?>, String>(classAliases.size()); |
for (Map.Entry<String, Class<?>> entry : classAliases.entrySet()) { |
classAliasMap.put(entry.getValue(), entry.getKey()); |
} |
this.strictDoubleParse = strictDoubleParse; |
this.indent = indent; |
this.withMetadata = withClassMetadata || withMetadata; |
this.failOnMissingProperty = failOnMissingProperty; |
} |
/** |
* Provides an instance of Converter capable of handling objects of type forType. |
* |
* @param forType the type for which a converter is needed. |
* @return the converter instance. |
* @throws com.owlike.genson.JsonBindingException if a problem occurs during converters lookup/construction. |
*/ |
@SuppressWarnings("unchecked") |
public <T> Converter<T> provideConverter(Type forType) { |
if (Boolean.TRUE.equals(ThreadLocalHolder.get("__GENSON$DO_NOT_CACHE_CONVERTER", Boolean.class))) { |
return (Converter<T>) converterFactory.create(forType, this); |
} else { |
Converter<T> converter = (Converter<T>) converterCache.get(forType); |
if (converter == null) { |
converter = (Converter<T>) converterFactory.create(forType, this); |
if (converter == null) |
throw new JsonBindingException("No converter found for type " + forType); |
converterCache.putIfAbsent(forType, converter); |
} |
return converter; |
} |
} |
/** |
* Serializes the object into a json string. |
* |
* @param object object to be serialized. |
* @return the serialized object as a string. |
* @throws com.owlike.genson.JsonBindingException if there was any kind of error during serialization. |
* @throws JsonStreamException if there was a problem during writing of the object to the output. |
*/ |
public String serialize(Object object) { |
StringWriter sw = new StringWriter(); |
ObjectWriter writer = createWriter(sw); |
if (object == null) serializeNull(writer); |
else serialize(object, object.getClass(), writer, new Context(this)); |
return sw.toString(); |
} |
/** |
* Serializes the object using the type of GenericType instead of using its runtime type. |
* |
* @param object object to be serialized. |
* @param type the type of the object to be serialized. |
* @return json string representation. |
* @throws com.owlike.genson.JsonBindingException |
* @throws JsonStreamException |
*/ |
public String serialize(Object object, GenericType<?> type) { |
StringWriter sw = new StringWriter(); |
ObjectWriter writer = createWriter(sw); |
if (object == null) serializeNull(writer); |
else serialize(object, type.getType(), writer, new Context(this)); |
return sw.toString(); |
} |
/** |
* Serializes the object using the specified BeanViews. |
* |
* @param object |
* @param withViews the BeanViews to apply during this serialization. |
* @return the json string representing this object |
* @throws com.owlike.genson.JsonBindingException |
* @throws com.owlike.genson.stream.JsonStreamException |
* @see BeanView |
*/ |
public String serialize(Object object, Class<? extends BeanView<?>> firstView, Class<? extends BeanView<?>>... withViews) { |
StringWriter sw = new StringWriter(); |
ObjectWriter writer = createWriter(sw); |
List<Class<? extends BeanView<?>>> views = new ArrayList(withViews.length); |
for (Class<? extends BeanView<?>> view : withViews) views.add(view); |
views.add(firstView); |
if (object == null) serializeNull(writer); |
else serialize(object, object.getClass(), writer, new Context(this, views)); |
return sw.toString(); |
} |
/** |
* Serializes this object to the passed Writer, as Genson did not instantiate it, you are |
* responsible of calling close on it. |
*/ |
public void serialize(Object object, Writer writer) { |
ObjectWriter objectWriter = createWriter(writer); |
if (object == null) serializeNull(objectWriter); |
else serialize(object, object.getClass(), objectWriter, new Context(this)); |
} |
/** |
* Serializes this object to the passed OutputStream, as Genson did not instantiate it, you are |
* responsible of calling close on it. |
*/ |
public void serialize(Object object, OutputStream output) { |
ObjectWriter objectWriter = createWriter(output); |
if (object == null) serializeNull(objectWriter); |
else serialize(object, object.getClass(), objectWriter, new Context(this)); |
} |
/** |
* Serializes this object to its json form in a byte array. |
*/ |
public byte[] serializeBytes(Object object) { |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
ObjectWriter objectWriter = createWriter(baos); |
if (object == null) serializeNull(objectWriter); |
else serialize(object, object.getClass(), objectWriter, new Context(this)); |
return baos.toByteArray(); |
} |
/** |
* Serializes this object and writes its representation to writer. As you are providing the |
* writer instance you also must ensure to call flush and close on it when you are done. |
* |
* @param object |
* @param writer into which to write the serialized object. |
* @throws com.owlike.genson.JsonBindingException |
* @throws JsonStreamException |
*/ |
public void serialize(Object object, ObjectWriter writer, Context ctx) { |
if (object == null) serializeNull(writer); |
else serialize(object, object.getClass(), writer, ctx); |
} |
/** |
* Serializes this object and writes its representation to writer. As you are providing the |
* writer instance you also must ensure to call close on it when you are done. |
*/ |
public void serialize(Object object, Type type, ObjectWriter writer, Context ctx) { |
Serializer<Object> ser = provideConverter(type); |
try { |
ser.serialize(object, writer, ctx); |
writer.flush(); |
} catch (Exception e) { |
throw new JsonBindingException("Failed to serialize object of type " + type, e); |
} |
} |
private void serializeNull(ObjectWriter writer) { |
try { |
writer.writeNull(); |
writer.flush(); |
} catch (Exception e) { |
throw new JsonBindingException("Could not serialize null value.", e); |
} |
} |
/** |
* Deserializes fromSource String into an instance of toClass. |
* |
* @param fromSource source from which to deserialize. |
* @param toClass type into which to deserialize. |
* @throws com.owlike.genson.JsonBindingException |
* @throws JsonStreamException |
*/ |
public <T> T deserialize(String fromSource, Class<T> toClass) { |
return deserialize(GenericType.of(toClass), createReader(new StringReader(fromSource)), |
new Context(this)); |
} |
/** |
* Deserializes to an instance of T. GenericType is useful when you want to deserialize to a |
* list or map (or any other type with generics). |
* |
* @param fromSource |
* @param toType |
* @throws com.owlike.genson.JsonBindingException |
* @throws JsonStreamException |
* @see GenericType |
*/ |
public <T> T deserialize(String fromSource, GenericType<T> toType) { |
return deserialize(toType, createReader(new StringReader(fromSource)), new Context(this)); |
} |
/** |
* Deserializes the incoming json stream into an instance of T. |
* Genson did not create the instance of Reader so it will not be closed |
*/ |
public <T> T deserialize(Reader reader, GenericType<T> toType) { |
return deserialize(toType, createReader(reader), new Context(this)); |
} |
/** |
* Deserializes the incoming json stream into an instance of T. |
* Genson did not create the instance of Reader so it will not be closed |
*/ |
public <T> T deserialize(Reader reader, Class<T> toType) { |
return deserialize(GenericType.of(toType), createReader(reader), new Context(this)); |
} |
/** |
* Deserializes the incoming json stream into an instance of T. |
* Genson did not create the instance of InputStream so it will not be closed |
*/ |
public <T> T deserialize(InputStream input, Class<T> toType) { |
return deserialize(GenericType.of(toType), createReader(input), new Context(this)); |
} |
/** |
* Deserializes the incoming json stream into an instance of T. |
* Genson did not create the instance of InputStream so it will not be closed. |
*/ |
public <T> T deserialize(InputStream input, GenericType<T> toType) { |
return deserialize(toType, createReader(input), new Context(this)); |
} |
/** |
* Deserializes the incoming json byte array into an instance of T. |
*/ |
public <T> T deserialize(byte[] input, Class<T> toType) { |
return deserialize(GenericType.of(toType), createReader(input), new Context(this)); |
} |
/** |
* Deserializes the incoming json byte array into an instance of T. |
*/ |
public <T> T deserialize(byte[] input, GenericType<T> toType) { |
return deserialize(toType, createReader(input), new Context(this)); |
} |
public <T> T deserialize(String fromSource, GenericType<T> toType, Class<? extends BeanView<?>>... withViews) { |
StringReader reader = new StringReader(fromSource); |
return deserialize(toType, createReader(reader), |
new Context(this, Arrays.asList(withViews))); |
} |
public <T> T deserialize(String fromSource, Class<T> toType, Class<? extends BeanView<?>>... withViews) { |
StringReader reader = new StringReader(fromSource); |
return deserialize(GenericType.of(toType), createReader(reader), |
new Context(this, Arrays.asList(withViews))); |
} |
public <T> T deserialize(GenericType<T> type, Reader reader, Class<? extends BeanView<?>>... withViews) { |
return deserialize(type, createReader(reader), new Context(this, Arrays.asList(withViews))); |
} |
public <T> T deserialize(GenericType<T> type, ObjectReader reader, Context ctx) { |
Deserializer<T> deser = provideConverter(type.getType()); |
try { |
return deser.deserialize(reader, ctx); |
} catch (Exception e) { |
throw new JsonBindingException("Could not deserialize to type " + type.getRawClass(), e); |
} |
} |
/** |
* @see #deserializeInto(com.owlike.genson.stream.ObjectReader, Object, Context) |
*/ |
public <T> T deserializeInto(String json, T object) { |
return deserializeInto(createReader(new StringReader(json)), object, new Context(this)); |
} |
/** |
* @see #deserializeInto(com.owlike.genson.stream.ObjectReader, Object, Context) |
*/ |
public <T> T deserializeInto(byte[] jsonBytes, T object) { |
return deserializeInto(createReader(jsonBytes), object, new Context(this)); |
} |
/** |
* @see #deserializeInto(com.owlike.genson.stream.ObjectReader, Object, Context) |
*/ |
public <T> T deserializeInto(InputStream is, T object) { |
return deserializeInto(createReader(is), object, new Context(this)); |
} |
/** |
* @see #deserializeInto(com.owlike.genson.stream.ObjectReader, Object, Context) |
*/ |
public <T> T deserializeInto(Reader reader, T object) { |
return deserializeInto(createReader(reader), object, new Context(this)); |
} |
/** |
* Deserializes the stream in the existing object. Note however that this works only for Pojos |
* and doesn't handle nested objects (will be overridden by the values from the stream). |
* |
* @return the object enriched with the properties from the stream. |
*/ |
public <T> T deserializeInto(ObjectReader reader, T object, Context ctx) { |
BeanDescriptor<T> bd = (BeanDescriptor<T>) getBeanDescriptorProvider().provide(object.getClass(), this); |
bd.deserialize(object, reader, ctx); |
return object; |
} |
/** |
* @see #deserializeValues(com.owlike.genson.stream.ObjectReader, GenericType) |
*/ |
public <T> Iterator<T> deserializeValues(final InputStream is, final Class<T> type) { |
return deserializeValues(createReader(is), GenericType.of(type)); |
} |
/** |
* This can be used to deserialize in an efficient streaming fashion a sequence of objects. |
* Note that you can use this method when your values are wrapped in an array (valid json) but also |
* when they all are root values (not enclosed in an array). For example: |
* |
* <pre> |
* Genson genson = new Genson(); |
* ObjectReader reader = genson.createReader(json); |
* |
* for (Iterator<LogEntry> it = genson.deserializeValues(reader, GenericType.of(LogEntry.class)); |
* it.hasNext(); ) { |
* // do something |
* LogEntry p = it.next(); |
* } |
* </pre> |
* @param reader an instance of the ObjectReader to use (obtained with genson.createReader(...) for ex.), |
* note that you are responsible of closing it. |
* @param type to deserialize to |
* @param <T> |
* @return an iterator of T |
*/ |
public <T> Iterator<T> deserializeValues(final ObjectReader reader, final GenericType<T> type) { |
final boolean isArray = reader.getValueType() == ValueType.ARRAY; |
if (isArray == true) { |
reader.beginArray(); |
} |
return new Iterator<T>() { |
final Converter<T> converter = provideConverter(type.getType()); |
final Context ctx = new Context(Genson.this); |
@Override |
public boolean hasNext() { |
boolean hasMore = reader.hasNext(); |
if (isArray && !hasMore) reader.endArray(); |
return hasMore; |
} |
@Override |
public T next() { |
if (!hasNext()) throw new NoSuchElementException(); |
reader.next(); |
try { |
return converter.deserialize(reader, ctx); |
} catch (Exception e) { |
throw new JsonBindingException("Could not deserialize to type " + type.getRawClass(), e); |
} |
} |
@Override |
public void remove() { |
throw new UnsupportedOperationException(); |
} |
}; |
} |
/** |
* Searches if an alias has been registered for clazz. If not will take the class full name and |
* use it as alias. This method never returns null. |
*/ |
public <T> String aliasFor(Class<T> clazz) { |
String alias = classAliasMap.get(clazz); |
if (alias == null) { |
alias = clazz.getName(); |
classAliasMap.put(clazz, alias); |
} |
return alias; |
} |
/** |
* Searches for the class matching this alias, if none will try to use the alias as the class |
* name. |
* |
* @param alias |
* @return The class matching this alias. |
* @throws ClassNotFoundException thrown if no class has been registered for this alias and the alias it self does |
* not correspond to the full name of a class. |
*/ |
public Class<?> classFor(String alias) throws ClassNotFoundException { |
Class<?> clazz = aliasClassMap.get(alias); |
if (clazz == null) { |
clazz = Class.forName(alias); |
aliasClassMap.put(alias, clazz); |
} |
return clazz; |
} |
/** |
* Creates a new ObjectWriter with this Genson instance configuration and default encoding to |
* UTF8. |
*/ |
public ObjectWriter createWriter(OutputStream os) { |
return new JsonWriter(new OutputStreamWriter(os, UTF8_CHARSET), skipNull, htmlSafe, indent); |
} |
/** |
* Creates a new ObjectWriter with this Genson instance configuration. |
*/ |
public ObjectWriter createWriter(OutputStream os, Charset charset) { |
return createWriter(new OutputStreamWriter(os, charset)); |
} |
/** |
* Creates a new ObjectWriter with this Genson instance configuration. |
*/ |
public ObjectWriter createWriter(Writer writer) { |
return new JsonWriter(writer, skipNull, htmlSafe, indent); |
} |
/** |
* @see #createReader(java.io.InputStream) |
*/ |
public ObjectReader createReader(byte[] in) { |
try { |
return createReader(readerFactory.createReader(new ByteArrayInputStream(in))); |
} catch (IOException e) { |
throw new JsonStreamException("Failed to detect encoding.", e); |
} |
} |
/** |
* Creates a new ObjectReader with this Genson instance configuration and tries to detect the encoding |
* from the stream content. |
*/ |
public ObjectReader createReader(InputStream is) { |
try { |
return createReader(readerFactory.createReader(is)); |
} catch (IOException e) { |
throw new JsonStreamException("Failed to detect encoding.", e); |
} |
} |
/** |
* Creates a new ObjectReader with this Genson instance configuration. |
*/ |
public ObjectReader createReader(InputStream is, Charset charset) { |
return createReader(new InputStreamReader(is, charset)); |
} |
/** |
* Creates a new ObjectReader with this Genson instance configuration. |
*/ |
public ObjectReader createReader(Reader reader) { |
return new JsonReader(reader, strictDoubleParse, withMetadata); |
} |
public boolean isSkipNull() { |
return skipNull; |
} |
public boolean isHtmlSafe() { |
return htmlSafe; |
} |
public boolean isWithClassMetadata() { |
return withClassMetadata; |
} |
public BeanDescriptorProvider getBeanDescriptorProvider() { |
return beanDescriptorFactory; |
} |
public boolean failOnMissingProperty() { |
return this.failOnMissingProperty; |
} |
/** |
* @return the defined default value for type clazz or null if none is defined. Intended for internal use. |
*/ |
public <T> T defaultValue(Class<T> clazz) { |
return (T) defaultValues.get(clazz); |
} |
public RuntimePropertyFilter runtimePropertyFilter() { |
return runtimePropertyFilter; |
} |
/** |
* @deprecated use GensonBuilder |
*/ |
@Deprecated |
public static class Builder extends GensonBuilder { |
} |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/JsonDateFormat.java |
---|
New file |
0,0 → 1,31 |
package com.owlike.genson.annotation; |
import java.lang.annotation.Documented; |
import java.lang.annotation.ElementType; |
import java.lang.annotation.Inherited; |
import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
import java.lang.annotation.Target; |
import java.text.SimpleDateFormat; |
/** |
* Can be used on java.util.Date and java.util.Calendar to indicate the pattern or lang to |
* use when working with this date field. The pattern format are the standard ones from |
* {@link SimpleDateFormat}. |
* |
* @author eugen |
*/ |
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) |
@Retention(RetentionPolicy.RUNTIME) |
@Inherited |
@Documented |
public @interface JsonDateFormat { |
/** |
* The pattern to use. |
*/ |
String value() default ""; |
boolean asTimeInMillis() default false; |
String lang() default ""; |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/JsonConverter.java |
---|
New file |
0,0 → 1,26 |
package com.owlike.genson.annotation; |
import java.lang.annotation.Documented; |
import java.lang.annotation.ElementType; |
import java.lang.annotation.Inherited; |
import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
import java.lang.annotation.Target; |
import com.owlike.genson.Converter; |
/** |
* This annotation is useful when you want to use a specific Converter for a property in a class, |
* but do not want to use it for all properties of that type. When you put this annotation on a |
* field, constructor parameter or setter/getter, Genson will use this Converter instead of any other. |
* <b>The Converter must have a default no arg constructor.</b> |
* |
* @author eugen |
*/ |
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) |
@Retention(RetentionPolicy.RUNTIME) |
@Inherited |
@Documented |
public @interface JsonConverter { |
Class<? extends Converter<?>> value(); |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/HandleClassMetadata.java |
---|
New file |
0,0 → 1,31 |
package com.owlike.genson.annotation; |
import java.lang.annotation.Documented; |
import java.lang.annotation.ElementType; |
import java.lang.annotation.Inherited; |
import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
import java.lang.annotation.Target; |
/** |
* Annotated Serializer/Deserializer/Converter with @HandleClassMetadata indicate that they will |
* handle @class metadata during serialization and deserialization. By default it is handled by the |
* library in {@link com.owlike.genson.convert.ClassMetadataConverter ClassMetadataConverter}. Default |
* converters from {@link com.owlike.genson.convert.DefaultConverters DefaultConverters} annotated with @HandleClassMetadata |
* do not serialize type information nor do they use it during deserialization. For security reasons |
* class metadata is disabled by default. To enable it |
* {@link com.owlike.genson.GensonBuilder#useClassMetadata(boolean)} |
* GensonBuilder.useClassMetadata(true)} |
* |
* @author eugen |
* @see com.owlike.genson.convert.ClassMetadataConverter ClassMetadataConverter |
* @see com.owlike.genson.GensonBuilder#useClassMetadata(boolean) |
* Genson.Builder.setWithClassMetadata(true) |
*/ |
@Target({ElementType.TYPE}) |
@Retention(RetentionPolicy.RUNTIME) |
@Inherited |
@Documented |
public @interface HandleClassMetadata { |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/package-info.java |
---|
New file |
0,0 → 1,4 |
/** |
* This package provides useful annotations to configure some features used during serialization and deserialization. |
*/ |
package com.owlike.genson.annotation; |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/HandleNull.java |
---|
New file |
0,0 → 1,27 |
package com.owlike.genson.annotation; |
import java.lang.annotation.Documented; |
import java.lang.annotation.ElementType; |
import java.lang.annotation.Inherited; |
import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
import java.lang.annotation.Target; |
/** |
* Similar to {@link HandleClassMetadata}, put this annotation on your Converters, Serializers and |
* Deserializers to disable Genson default null handling ( |
* {@link com.owlike.genson.convert.NullConverter NullConverter}). In that case you will have to |
* write the code that handles nulls during serialization and deserialization of your type (and not |
* of its content). This feature is mainly for internal use. |
* |
* @author eugen |
* @see HandleClassMetadata |
* @see com.owlike.genson.convert.NullConverter NullConverter |
*/ |
@Target({ElementType.TYPE}) |
@Retention(RetentionPolicy.RUNTIME) |
@Inherited |
@Documented |
public @interface HandleNull { |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/JsonCreator.java |
---|
New file |
0,0 → 1,33 |
package com.owlike.genson.annotation; |
import java.lang.annotation.*; |
/** |
* Static methods annotated with @JsonCreator annotation will act as method factories. These methods can |
* take arguments that match properties from the json stream. If you use default configuration you |
* must annotate each argument with {@link com.owlike.genson.annotation.JsonProperty JsonProperty} and |
* define a name. However Genson is also able to use the names from the method signature, but by |
* default it is disabled. To enable this feature use |
* <p/> |
* <pre> |
* new GensonBuilder().useConstructorWithArguments(true).create(); |
* </pre> |
* <p/> |
* It will register {@link com.owlike.genson.reflect.ASMCreatorParameterNameResolver |
* ASMCreatorParameterNameResolver} name resolver, that will use the debug symbols generated during |
* compilation to resolve the names. |
* <p/> |
* By default if a object contains constructors and methods annotated with @JsonCreator the factory |
* methods will be privileged. |
* |
* @author eugen |
* @see com.owlike.genson.annotation.JsonProperty JsonProperty |
* @see com.owlike.genson.reflect.BeanMutatorAccessorResolver.StandardMutaAccessorResolver |
* StandardMutaAccessorResolver |
*/ |
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE}) |
@Retention(RetentionPolicy.RUNTIME) |
@Inherited |
@Documented |
public @interface JsonCreator { |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/HandleBeanView.java |
---|
New file |
0,0 → 1,23 |
package com.owlike.genson.annotation; |
import java.lang.annotation.Documented; |
import java.lang.annotation.ElementType; |
import java.lang.annotation.Inherited; |
import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
import java.lang.annotation.Target; |
/** |
* Annotated Serializer/Deserializer/Converter will be excluded from the BeanView mechanism. |
* Most default converters are annotated with HandleBeanView (IntegerConverter, BooleanConverter etc). |
* |
* @author eugen |
* @see com.owlike.genson.convert.BeanViewConverter BeanViewConverter |
*/ |
@Target({ElementType.TYPE}) |
@Retention(RetentionPolicy.RUNTIME) |
@Inherited |
@Documented |
public @interface HandleBeanView { |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/WithBeanView.java |
---|
New file |
0,0 → 1,26 |
package com.owlike.genson.annotation; |
import java.lang.annotation.Documented; |
import java.lang.annotation.ElementType; |
import java.lang.annotation.Inherited; |
import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
import java.lang.annotation.Target; |
import com.owlike.genson.BeanView; |
/** |
* Annotation used actually only in spring web integration |
* {@link com.owlike.genson.ext.spring.GensonMessageConverter GensonMessageConverter} to indicate |
* at runtime what BeanView must be used. Its intended to be used in conjunction |
* with springs @ResponseBody/@RequestBody and @RequestMapping annotations. |
* |
* @author eugen |
*/ |
@Target({ElementType.METHOD}) |
@Retention(RetentionPolicy.RUNTIME) |
@Inherited |
@Documented |
public @interface WithBeanView { |
Class<? extends BeanView<?>>[] views() default {}; |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/JsonIgnore.java |
---|
New file |
0,0 → 1,35 |
package com.owlike.genson.annotation; |
import java.lang.annotation.Documented; |
import java.lang.annotation.ElementType; |
import java.lang.annotation.Inherited; |
import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
import java.lang.annotation.Target; |
/** |
* You can annotate with @JsonIgnore the methods, fields and creators that must be ignored during |
* serialization AND deserialization. To exclude property from only deserialization and keep it |
* during serialization use @JsonIgnore(serialize=true), for example if you annotate a field with |
* @JsonIgnore(serialize=true,deserialize=true) it will have no effect! |
* |
* @author eugen |
* @see com.owlike.genson.annotation.JsonProperty JsonProperty |
*/ |
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD}) |
@Retention(RetentionPolicy.RUNTIME) |
@Inherited |
@Documented |
public @interface JsonIgnore { |
/** |
* Whether to include this property in serialization. False by default, the property won't be |
* serialized. |
*/ |
boolean serialize() default false; |
/** |
* Whether to include this property in deserialization. False by default, the property won't be |
* deserialized. |
*/ |
boolean deserialize() default false; |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/annotation/JsonProperty.java |
---|
New file |
0,0 → 1,50 |
package com.owlike.genson.annotation; |
import java.lang.annotation.Documented; |
import java.lang.annotation.ElementType; |
import java.lang.annotation.Inherited; |
import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
import java.lang.annotation.Target; |
/** |
* JsonProperty annotation can be used to define the name of a property. You can apply it on fields |
* and methods. In that case this name will be used instead of the conventional one computed from |
* the signature. You can also use this annotation on parameters of creator methods and on |
* constructor parameters. In that case Genson during deserialization will try to use those names to |
* match the properties from the json stream. By default it is used in |
* {@link com.owlike.genson.reflect.PropertyNameResolver.AnnotationPropertyNameResolver |
* AnnotationPropertyNameResolver}. |
* |
* @author eugen |
* @see com.owlike.genson.reflect.PropertyNameResolver.AnnotationPropertyNameResolver |
* AnnotationPropertyNameResolver |
* @see com.owlike.genson.annotation.JsonCreator JsonCreator |
* @see com.owlike.genson.annotation.JsonIgnore JsonIgnore |
*/ |
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) |
@Retention(RetentionPolicy.RUNTIME) |
@Inherited |
@Documented |
public @interface JsonProperty { |
/** |
* The name of that property. |
*/ |
String value() default ""; |
/** |
* A list of aliases to use during deserialization for this property. Note that during serialization this is not used. |
*/ |
String[] aliases() default {}; |
/** |
* Whether this property must be serialized. Default is true, the property will be serialized. |
*/ |
boolean serialize() default true; |
/** |
* Whether this property must be deserialized. Default is true, the property will be |
* deserialized. |
*/ |
boolean deserialize() default true; |
} |
/branches/grupo4/impl/src/java/com/owlike/genson/Trilean.java |
---|
New file |
0,0 → 1,34 |
package com.owlike.genson; |
/** |
* A boolean with 3 states : true, false and unknown. |
* |
* @author eugen |
*/ |
public enum Trilean { |
TRUE() { |
@Override |
public boolean booleanValue() { |
return true; |
} |
}, |
FALSE { |
@Override |
public boolean booleanValue() { |
return false; |
} |
}, |
UNKNOWN { |
@Override |
public boolean booleanValue() { |
throw new IllegalStateException( |
"Unknown state can not be converter to a boolean, only TRUE AND FALSE can!"); |
} |
}; |
public static Trilean valueOf(boolean value) { |
return value ? TRUE : FALSE; |
} |
public abstract boolean booleanValue(); |
} |
/branches/grupo4/impl/src/hbm/pt/estgp/estgweb/domain/Course.hbm.xml |
---|
79,6 → 79,10 |
<property name="separatedTurmas" type="boolean"> |
<column name="separatedTurmas" default="false"/> |
</property> |
<!-- Ponto 1 Criaçao de Variavel--> |
<property name="courseReportDocument" type="string"> |
<column name="courseReportDocument" sql-type="LONGTEXT"/> |
</property> |
<many-to-one name="course" class="pt.estgp.estgweb.domain.Course" outer-join="true" lazy="false" column="course_id"/> |
<subclass name="pt.estgp.estgweb.domain.CourseYearImpl" discriminator-value="CourseYearImpl"/> |
</class> |
/branches/grupo4/impl/src/web/hello.jsp |
---|
File deleted |
/branches/grupo4/impl/src/web/user/courses/courseReportEdit.jsp |
---|
314,30 → 314,9 |
} |
} |
$scope.save = function() |
{ |
widgetCallWithActionParameters( |
"<%=request.getContextPath()%>/user/courseReport.do", |
"save", |
{ |
"report" : BacoJS.stringifyOrdered($scope.report) |
}, |
"#courseReportApp", |
function(resposta) |
{ |
$scope.report = resposta; |
alert(resposta.courseName); |
$scope.$apply(); |
}, |
function(){} |
); |
} |
}); |
</script> |
352,7 → 331,6 |
</div> |
<button ng-click="save()" class="btn btn-success">Salvar</button> |
<!-- <pre class="code">{{ report | json }}</pre>--> |
/branches/grupo4/impl/libs.xml |
---|
98,7 → 98,7 |
</fileset> |
<fileset dir="${common.lib.dir}/json"> |
<include name="**/*.jar"/> |
<!--<exclude name="genson-1.4.jar"/>--> |
<exclude name="genson-1.4.jar"/> |
<exclude name="genson-0.97.jar"/> |
</fileset> |
<fileset dir="${common.lib.dir}/jsoup"> |
/branches/grupo4/impl/build.xml |
---|
66,6 → 66,7 |
<target name="initDirs"> |
<mkdir dir="${log.dir}"/> |
<mkdir dir="${data.dir}"/> |
<mkdir dir="${tmp.dir}"/> |
<mkdir dir="${build.dir}"/> |
<mkdir dir="${build.dir.classes}"/> |
<mkdir dir="${build.dir.war}"/> |
299,7 → 300,7 |
<fileset dir="${common.lib.dir}/json"> |
<include name="**/*.jar"/> |
<exclude name="genson-0.97.jar"/> |
<!--<exclude name="genson-1.4.jar"/>--> |
<exclude name="genson-1.4.jar"/> |
</fileset> |
<fileset dir="${common.lib.dir}/jsoup"> |
<include name="**/*.jar"/> |
/branches/grupo4/impl/gen/java/pt/estgp/estgweb/domain/CourseYear.java |
---|
2,12 → 2,13 |
// Generated 4/nov/2017 19:35:42 by Hibernate Tools 3.2.0.b9 |
import java.io.Serializable; |
import java.util.Date; |
/** |
* CourseYear generated by hbm2java |
*/ |
public abstract class CourseYear extends pt.estgp.estgweb.domain.DomainSerializableObject implements java.io.Serializable { |
public abstract class CourseYear extends DomainSerializableObject implements Serializable { |
private long id; |
16,7 → 17,16 |
private String importYear; |
private boolean separatedTurmas; |
private Course course; |
private String courseReportDocument; |
public String getCourseReportDocument() { |
return courseReportDocument; |
} |
public void setCourseReportDocument(String courseReportDocument) { |
this.courseReportDocument = courseReportDocument; |
} |
public CourseYear() { |
} |
/branches/grupo17529/impl/conf/berserk/sd.xml |
---|
4048,6 → 4048,20 |
</filterChains> |
</service> |
<service> |
<name>CourseReportSave</name> |
<implementationClass>pt.estgp.estgweb.services.courses.CourseReportServices</implementationClass> |
<description> |
@reportCourseDocumento faz save de um documento alterado |
</description> |
<isTransactional>true</isTransactional> |
<defaultMethod>saveCourseReport</defaultMethod> |
<filterChains> |
<chain name="Logger"/> |
<chain name="Session"/> |
</filterChains> |
</service> |
</serviceDefinitions> |
/branches/grupo17529/impl/src/java/pt/estgp/estgweb/services/courses/CourseReportServices.java |
---|
634,18 → 634,21 |
} |
} |
//save |
public String saveCourseReport(String reportDocumentJson,UserSession session){ |
CourseReportDocument reportDocument = CourseReportDocument.fromJson(reportDocumentJson); |
return null; |
} |
public static void main(String[] args) throws IOException, JSONException { |
AbstractDao.getCurrentSession().beginTransaction(); |
/branches/grupo17529/impl/src/hbm/pt/estgp/estgweb/domain/Course.hbm.xml |
---|
79,6 → 79,10 |
<property name="separatedTurmas" type="boolean"> |
<column name="separatedTurmas" default="false"/> |
</property> |
<!-- Ponto 1 Criaçao de Variavel--> |
<property name="courseReportDocument" type="string"> |
<column name="courseReportDocument" sql-type="LONGTEXT"/> |
</property> |
<many-to-one name="course" class="pt.estgp.estgweb.domain.Course" outer-join="true" lazy="false" column="course_id"/> |
<subclass name="pt.estgp.estgweb.domain.CourseYearImpl" discriminator-value="CourseYearImpl"/> |
</class> |
/branches/grupo17529/impl/build.xml |
---|
66,6 → 66,7 |
<target name="initDirs"> |
<mkdir dir="${log.dir}"/> |
<mkdir dir="${data.dir}"/> |
<mkdir dir="${tmp.dir}"/> |
<mkdir dir="${build.dir}"/> |
<mkdir dir="${build.dir.classes}"/> |
<mkdir dir="${build.dir.war}"/> |
/branches/grupo17529/impl/gen/java/pt/estgp/estgweb/domain/CourseYear.java |
---|
2,12 → 2,13 |
// Generated 4/nov/2017 19:35:42 by Hibernate Tools 3.2.0.b9 |
import java.io.Serializable; |
import java.util.Date; |
/** |
* CourseYear generated by hbm2java |
*/ |
public abstract class CourseYear extends pt.estgp.estgweb.domain.DomainSerializableObject implements java.io.Serializable { |
public abstract class CourseYear extends DomainSerializableObject implements Serializable { |
private long id; |
16,7 → 17,16 |
private String importYear; |
private boolean separatedTurmas; |
private Course course; |
private String courseReportDocument; |
public String getCourseReportDocument() { |
return courseReportDocument; |
} |
public void setCourseReportDocument(String courseReportDocument) { |
this.courseReportDocument = courseReportDocument; |
} |
public CourseYear() { |
} |
/branches/grupo11/impl/conf/WEB-INF/struts/struts-courses.xml |
---|
164,7 → 164,8 |
<action path="/user/courseReportTools" forward="page.course.report.tools"/> |
<action path="/user/loadCourseReportTools" forward="page.course.report.tools.load"/> |
<action path="/user/editCourseReport" forward="page.course.report.edit"/> |
<!--Chama o servico de edição --> |
<action path="/user/editCourseReport" forward="/user/courseReport.do?dispatch=loadReportDocument"/> |
<action path="/user/courseReport" |
type="pt.estgp.estgweb.web.controllers.courses.CoursesServicesController" |
173,9 → 174,15 |
parameter="dispatch" |
validate="true" |
input="page.widget.json.fail.validations"> |
<!--Dispacho para a mesma pagina de edição --> |
<forward name="editReport" path="page.course.report.edit"/> |
</action> |
</action-mappings> |
</struts-config> |
/branches/grupo11/impl/conf/berserk/sd.xml |
---|
4022,11 → 4022,14 |
<defaultMethod>loadPlanYearForCourseUnitCode</defaultMethod> |
<filterChains> |
<chain name="Logger"/> |
<chain name="Session"/> |
<chain name="CoordinatorCourse"/> |
</filterChains> |
</service> |
<!-- SERVICOS DE GERACAO DE RELATORIO DE CURSO --> |
4045,9 → 4048,50 |
<filterChains> |
<chain name="Logger"/> |
<chain name="Session"/> |
</filterChains> |
</service> |
<!-- ServicosExameES --> |
<service> |
<name>CourseReportDocumentSave</name> |
<implementationClass>pt.estgp.estgweb.services.courses.CourseReportServices</implementationClass> |
<description> |
@reportCourseDocument documento course report em JSON |
Servico para guardar um courseReport na base de dados |
Guarda o documento do curso |
</description> |
<filterChains> |
<chain name="Logger"/> <!--Log dos acessos --> |
<!-- chain name "Session" /> --> |
<!-- chain name="CoordinatorCourse"/>/ --> |
</filterChains> |
<isTransactional>true</isTransactional> |
<defaultMethod>saveReportDocument</defaultMethod> |
</service> |
<service> |
<name>CourseReportDocumentLoad</name> |
<implementationClass>pt.estgp.estgweb.services.courses.CourseReportServices</implementationClass> |
<description> |
@courseCode código do curso |
@year import year ( ano do report) |
Servico para fazer load a um courseReport da base de dados |
Carrega o documento do curso |
</description> |
<filterChains> |
<chain name = "Logger"/> |
<chain name = "Session"/> |
<chain name = "CoordinatorCourse"/> |
</filterChains> |
<isTransactional>true</isTransactional> |
<defaultMethod>loadReportDocument</defaultMethod> |
</service> |
</serviceDefinitions> |
/branches/grupo11/impl/conf/berserk/fd.xml |
---|
100,6 → 100,7 |
</description> |
<isTransactional>false</isTransactional> |
</filter> |
<filter> |
<name>IsTeacherInCourseUnitUsersClass</name> |
<implementationClass>pt.estgp.estgweb.filters.filters.IsTeacherInCourseUnitUsersClass</implementationClass> |
182,7 → 183,7 |
<name>LogAccess</name> |
<implementationClass>pt.estgp.estgweb.filters.filters.AccessLogger</implementationClass> |
<description>Writes accesses to a file</description> |
<isTransactional>false</isTransactional> |
<isTransactional>false</isTransactional>f |
</filter> |
<filter> |
<name>SessionLoad</name> |
/branches/grupo11/impl/conf/berserk/fcd.xml |
---|
56,7 → 56,15 |
<invocationTiming>1</invocationTiming> |
<filterClass>pt.estgp.estgweb.filters.chains.AdminControlFilter</filterClass> |
</filterChain> |
<!-- My Filter Chain --> |
<filterChain> |
<name>CoordinatorCourse</name> |
<expression>AuthenticatedUsers && RoleUsers("teacher,courseCoordinator") || RoleUsers("super")</expression> |
<description>Validate if user is authenticated and is a teacher or a super</description> |
<invocationTiming>1</invocationTiming> |
<filterClass>pt.estgp.estgweb.filters.chains.AdminControlFilter</filterClass> |
</filterChain> |
<filterChain> |
<name>DirectorsCoordinators</name> |
<expression>AuthenticatedUsers</expression> |
<description>Validate if a user is coordinator or director</description> |
/branches/grupo11/impl/src/java/pt/estgp/estgweb/services/courses/CourseReportServices.java |
---|
9,6 → 9,10 |
import org.json.JSONObject; |
import pt.estgp.estgweb.domain.*; |
import pt.estgp.estgweb.domain.dao.DaoFactory; |
import pt.estgp.estgweb.domain.dao.DaoUtils; |
import pt.estgp.estgweb.domain.dao.impl.CourseDao; |
import pt.estgp.estgweb.domain.dao.impl.CourseDaoImpl; |
import pt.estgp.estgweb.domain.dao.impl.CourseYearDaoImpl; |
import pt.estgp.estgweb.filters.chains.ResourceAccessControlEnum; |
import pt.estgp.estgweb.services.courses.coursereport.CourseReportUtils; |
import pt.estgp.estgweb.services.courses.coursereport.documentmodel.*; |
29,8 → 33,10 |
import pt.estgp.estgweb.web.controllers.utils.FileUploaded; |
import pt.utl.ist.berserk.logic.serviceManager.IService; |
import javax.swing.text.View; |
import java.io.IOException; |
import java.io.InputStream; |
import java.io.Serializable; |
import java.net.URL; |
import java.net.URLConnection; |
import java.util.*; |
337,6 → 343,8 |
for(CourseUnit cu :units) |
{ |
CourseUnitDtpStat statFound = CourseReportUtils.findCourseUnitDtpStat(statsLoaded, (CourseUnitImpl) cu); |
if(statFound == null) |
{ |
statFound = CourseReportUtils.createCourseUnitDtpStat(cu); |
450,9 → 458,78 |
/* Save Method */ |
public String saveReportDocument (String reportDocumentJson,UserSession session){ |
CourseReportDocument reportDocument = CourseReportDocument.fromJson(reportDocumentJson); |
List<CourseYear> cyList = DaoFactory.getCourseYearDaoImpl().findCourseYear(reportDocument.getCourseCode(),reportDocument.getYear()); |
if(cyList!= null){ |
for (CourseYear courseYear : cyList) { |
courseYear.setCourseReportDocument(reportDocument.toJson()); |
} |
DaoFactory.getCourseYearDaoImpl().save(cyList); |
} |
else |
{ |
CourseYear cy = DomainObjectFactory.createCourseYearImpl(); |
cy.setCourseReportDocument(reportDocument.toJson()); |
Course course = DaoFactory.getCourseDaoImpl().findCourseByCodeAndYear(reportDocument.getCourseCode(),reportDocument.getYear()); |
cy.setImportYear(reportDocument.getYear()); |
cy.setSaveDate(session.getSaveDate()); |
course.getCourseYears().add(cy); |
cy.setCourse(course); |
DaoFactory.getCourseYearDaoImpl().save(cy); |
} |
return reportDocumentJson; |
} |
/* LOAD METHOD */ |
public String loadReportDocument(String courseCode, String year, UserSession session){ |
CourseYear cy = DaoFactory.getCourseYearDaoImpl().findCourseYear(courseCode,year).get(0); |
CourseReportDocument reportDocument = null; |
String reportDocumentJson = null; |
if(cy!= null){ |
reportDocumentJson = cy.getCourseReportDocument(); |
} |
else{ |
try { |
reportDocument = createNewCourseReportDocument(courseCode,year); |
saveReportDocument(reportDocumentJson,session); |
reportDocumentJson = reportDocument.toJson(); |
} catch (IOException e) { |
e.printStackTrace(); |
} catch (JSONException e) { |
e.printStackTrace(); |
} |
} |
return reportDocumentJson; |
} |
/****************************************************************************/ |
/* |
641,11 → 718,6 |
public static void main(String[] args) throws IOException, JSONException { |
AbstractDao.getCurrentSession().beginTransaction(); |
/branches/grupo11/impl/src/java/pt/estgp/estgweb/web/controllers/courses/CoursesServicesController.java |
---|
1,7 → 1,13 |
package pt.estgp.estgweb.web.controllers.courses; |
import jomm.dao.impl.AbstractDao; |
import org.apache.struts.action.ActionForm; |
import org.apache.struts.action.ActionForward; |
import org.apache.struts.action.ActionMapping; |
import org.json.JSONObject; |
import pt.estgp.estgweb.domain.CourseImpl; |
import pt.estgp.estgweb.domain.dao.DaoFactory; |
import pt.estgp.estgweb.services.courses.coursereport.documentmodel.CourseReportDocument; |
import pt.estgp.estgweb.web.controllers.utils.AbstractWidgetAjaxController; |
import pt.estgp.estgweb.web.utils.RequestUtils; |
import pt.utl.ist.berserk.logic.serviceManager.IServiceManager; |
43,4 → 49,30 |
} |
public ActionForward loadReportDocument(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Throwable{ |
String courseCode = request.getParameter("courseCode"); |
String year = request.getParameter("year"); |
AbstractDao.getCurrentSession().beginTransaction(); |
CourseImpl courseImpl = DaoFactory.getCourseDaoImpl().findCourseByCode(courseCode); |
request.setAttribute("course",courseImpl); |
IServiceManager sm = ServiceManager.getInstance(); |
String[] names = new String[]{courseCode,year}; |
Object[] args = new Object[] {courseCode,year}; |
String documentJson = (String) sm.execute(RequestUtils.getRequester(request,response),"loadReportDocument", args,names); |
request.setAttribute("courseDocumentJson",documentJson); |
AbstractDao.getCurrentSession().getTransaction().commit(); |
return mapping.findForward("editReport"); |
} |
} |
/branches/grupo11/impl/src/hbm/pt/estgp/estgweb/domain/Course.hbm.xml |
---|
79,6 → 79,9 |
<property name="separatedTurmas" type="boolean"> |
<column name="separatedTurmas" default="false"/> |
</property> |
<property name="courseReportDocument" type="string"> |
<column name="courseReportDocument" sql-type="LONGTEXT"/> |
</property> |
<many-to-one name="course" class="pt.estgp.estgweb.domain.Course" outer-join="true" lazy="false" column="course_id"/> |
<subclass name="pt.estgp.estgweb.domain.CourseYearImpl" discriminator-value="CourseYearImpl"/> |
</class> |
/branches/grupo11/impl/src/web/user/courses/courseReportEdit.jsp |
---|
1,12 → 1,5 |
<%@ page contentType="text/html;charset=UTF-8" language="java" %> |
<%@ page import="jomm.dao.impl.AbstractDao" %> |
<%@ page import="pt.estgp.estgweb.domain.CourseImpl" %> |
<%@ page import="pt.estgp.estgweb.domain.dao.DaoFactory" %> |
<%@ page import="pt.estgp.estgweb.utils.documentBuilder.TextComponent" %> |
<%@ page import="pt.estgp.estgweb.utils.documentBuilder.ImageComponent" %> |
<%@ page import="pt.estgp.estgweb.services.courses.CourseReportServices" %> |
<%@ page import="pt.estgp.estgweb.services.courses.coursereport.documentmodel.CourseReportDocument" %> |
<%@ page import="org.json.JSONException" %> |
<%@ taglib uri="/WEB-INF/tlds/struts-logic.tld" prefix="logic" %> |
<%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %> |
<%@ taglib uri="/WEB-INF/tlds/jomm.tld" prefix="jomm" %> |
15,335 → 8,5 |
<%@ taglib uri="/WEB-INF/tlds/baco.tld" prefix="baco" %> |
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %> |
<link rel="stylesheet" href="<%=request.getContextPath()%>/js/jquery-ui-1.12.1/jquery-ui.css"> |
<script src="<%=request.getContextPath()%>/js/jquery-ui-1.12.1/jquery-ui.min.js"></script> |
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/flora-commons/flora.resizable.css"> |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.2/jspdf.min.js"></script> |
<script> |
function demoFromHTML() { |
var pdf = new jsPDF('p', 'pt', 'letter'); |
// source can be HTML-formatted string, or a reference |
// to an actual DOM element from which the text will be scraped. |
source = $('#courseReportApp')[0]; |
// we support special element handlers. Register them with jQuery-style |
// ID selector for either ID or node name. ("#iAmID", "div", "span" etc.) |
// There is no support for any other type of selectors |
// (class, of compound) at this time. |
specialElementHandlers = { |
// element with id of "bypass" - jQuery style selector |
'#bypassme': function (element, renderer) { |
// true = "handled elsewhere, bypass text extraction" |
return true |
} |
}; |
margins = { |
top: 80, |
bottom: 60, |
left: 40, |
width: 522 |
}; |
// all coords and widths are in jsPDF instance's declared units |
// 'inches' in this case |
pdf.fromHTML( |
source, // HTML string or DOM elem ref. |
margins.left, // x coord |
margins.top, { // y coord |
'width': margins.width, // max width of content on PDF |
'elementHandlers': specialElementHandlers |
}, |
function (dispose) { |
// dispose: object with X, Y of the last line add to the PDF |
// this allow the insertion of new lines after html |
//var pdfDocument = pdf.save('Test.pdf'); |
var pdfDocument = pdf.output(); |
var boundary = '---------------------------'; |
boundary += Math.floor(Math.random()*32768); |
boundary += Math.floor(Math.random()*32768); |
boundary += Math.floor(Math.random()*32768); |
var body = ''; |
body += '--' + boundary + '\r\n' + |
'Content-Disposition: form-data; name="filesInputId-UPLOAD[]"; filename="20170530_210340.pdf"' + '\r\n'; |
body += 'Content-Type: application/pdf'; |
body += '\r\n\r\n'; |
body += pdfDocument; |
body += '\r\n' |
body += '--' + boundary + '--'; |
$.ajax({ |
type: "POST", |
cache: false, |
url: "<%=request.getContextPath()%>/filesUpload", |
data: body , |
processData: false, |
contentType : 'multipart/form-data; boundary=' + boundary, |
success: function (data) { |
alert('success'); |
return false; |
} |
}); |
}, margins |
); |
} |
</script> |
<% |
String courseCode = request.getParameter("courseCode"); |
String year = request.getParameter("year"); |
AbstractDao.getCurrentSession().beginTransaction(); |
CourseImpl courseImpl = DaoFactory.getCourseDaoImpl().findCourseByCode(courseCode); |
request.setAttribute("course",courseImpl); |
CourseReportDocument courseReport = null; |
try { |
courseReport = new CourseReportServices().createNewCourseReportDocument(courseCode, year); |
} catch (Throwable e) { |
System.out.println(e); |
e.printStackTrace(); |
} |
String courseReportJson = courseReport.toJson(); |
request.setAttribute("courseDocumentJson",courseReportJson); |
request.setAttribute("courseDocument",courseReport); |
%> |
<%--<a href="javascript:demoFromHTML()" class="button">Run Code</a>--%> |
<div class="container-fluid"> |
<style> |
.separatorSection |
{ |
border: 1px solid #ddd; |
} |
</style> |
<!-- Apresentacao da Unidade --> |
<div class="panel panel-default"> |
<div class="panel-heading"> |
Relatório Anual do curso: ${course.name} |
</div> |
<div class="panel-body"> |
<p><b class="label-info">Tipo de Curso:</b> <bean:message key="course.${course.degree}"/></p> |
<p><b class="label-info">Ano Lectivo:</b> ${course.importYear}</p> |
<p><b class="label-info">Departamento:</b> ${course.department.name}</p> |
<p><b class="label-info">Escola:</b> ${course.department.courseSchool.name}</p> |
<script> |
//Especifico da aplicacao |
var courseReportApp = angular.module('courseReportApp', ['ui.tree']); |
GLOBAL_BacoAngularAppDependencies.push('courseReportApp'); |
courseReportApp.directive('resizable', function () { |
return { |
restrict: 'A', |
scope: { |
callback: '&onResize' |
}, |
link: function postLink(scope, elem, attrs) { |
elem.resizable(); |
elem.on('resize', function (evt, ui, comp) { |
scope.$apply(function() { |
if (scope.callback) { |
scope.callback({$evt: evt, $ui: ui, $comp: comp }); |
} |
}) |
}); |
} |
}; |
}); |
courseReportApp.controller('courseReportAppController', function($scope) |
{ |
$scope.docAppSelector = "#courseReportApp"; |
$scope.report = <%=courseReportJson%> |
$scope.resize = function(evt,ui,comp) { |
//console.log (evt,ui); |
comp.width = ui.size.width; |
comp.height = ui.size.height; |
} |
/** |
* @classe class to match |
* @superClasses array of strings |
* */ |
$scope.contains = function(obj,classe) |
{ |
if(obj['@class'] && obj['@class'] == classe) |
return true; |
if(obj.allSuperClasses) |
{ |
for(var i in obj.allSuperClasses) |
{ |
if(classe == obj.allSuperClasses[i]) |
return true; |
} |
} |
return false; |
} |
$scope.showSep = function(section,subSection) |
{ |
var s; |
for(s in section.sections) |
{ |
section.sections[s].active = false; |
} |
/*$(".separatorSectionNav").each(function() |
{ |
angular.element($(this)).scope().section.active = false; |
});*/ |
subSection.active = true; |
} |
/** |
* Este metodo devolve o template mais profundo na hierarquia de classes |
* permitindo emular o override, quanto mais especifica for a classe |
* e caso exista template é esse o template devolvido |
* procura um script com o id da classe e se nao existir |
* vai subindo nas super classes |
* @param obj |
* @returns {*} |
*/ |
$scope.class2id = function(obj) |
{ |
var objClassId = obj["@class"].replaceAll(".","_"); |
if($("script#" + objClassId).length > 0) |
{ |
return objClassId; |
} |
if(obj.allSuperClasses) |
{ |
var s; |
for(s in obj.allSuperClasses) |
{ |
var superClass = obj.allSuperClasses[s]; |
var superClassId = superClass.replaceAll(".","_"); |
if($("script#" + superClassId).length > 0) |
{ |
return superClassId; |
} |
} |
} |
return obj["@class"].replaceAll(".","_"); |
} |
$scope.addText = function(parentCustomPane) |
{ |
$scope.addSimpleDocComponent(parentCustomPane,"pt.estgp.estgweb.utils.documentBuilder.TextComponent") |
} |
$scope.addImage = function(parentCustomPane) |
{ |
$scope.addSimpleDocComponent(parentCustomPane,"pt.estgp.estgweb.utils.documentBuilder.ImageComponent") |
} |
$scope.addSimpleDocComponent = function(parentCustomPane,classComponent) |
{ |
if(!parentCustomPane.components) |
{ |
parentCustomPane.components = []; |
} |
parentCustomPane.components.push( |
{ |
"@class" : classComponent |
} |
); |
} |
$scope.removeComponent = function(index,array) |
{ |
array.splice(index,1); |
} |
$scope.callbackUploadedFiles = function(filesUploadResult,token,targetElement) |
{ |
var modelObject = BacoAngularUtils.getAngularElementModel(targetElement); |
if(modelObject.image && modelObject.image.identifier) |
{ |
widgetCallWithActionParameters( |
"<%=request.getContextPath()%>/user/json/repository.do", |
"replaceRepositoryFileFromTempPrivateDomain", |
{ |
"identifier" : modelObject.image.identifier, |
"fileUploaded" : BacoJS.stringifyOrdered(filesUploadResult.uploadedFiles[0]) |
}, |
"#courseReportApp", |
function(repositoryFile4JsonView) |
{ |
modelObject.image = repositoryFile4JsonView; |
//image URL is generated on reimport just to avoid caching |
modelObject.imageUrl = "<%=request.getContextPath()%>/repositoryStream/" + modelObject.image.identifier + "?" + new Date().getTime(); |
angular.element($("#courseReportApp")).scope().$apply(); |
}, |
function(){} |
); |
} |
else |
{ |
widgetCallWithActionParameters( |
"<%=request.getContextPath()%>/user/json/repository.do", |
"saveRepositoryFileFromTempPrivateDomain", |
{ |
"fileUploaded" : BacoJS.stringifyOrdered(filesUploadResult.uploadedFiles[0]) |
}, |
"#courseReportApp", |
function(repositoryFile4JsonView) |
{ |
modelObject.image = repositoryFile4JsonView; |
modelObject.imageUrl = "<%=request.getContextPath()%>/repositoryStream/" + modelObject.image.identifier + "?" + new Date().getTime(); |
angular.element($("#courseReportApp")).scope().$apply(); |
}, |
function(){} |
); |
} |
} |
}); |
</script> |
<!--TEMPLATES FOR DOCUMENT BUILDER--> |
<jsp:include page="../utils/documentsBuilder.jsp"/> |
<jsp:include page="../utils/reportEdit.jsp"/> |
<jsp:include page="coursereport/templates.jsp"/> |
<div class="form-vertical"> |
<div id="courseReportApp" ng-app="courseReportApp" ng-controller="courseReportAppController"> |
<div ng-init="section=report;" ng-include="'pt_estgp_estgweb_utils_documentBuilder_DocumentSection'"> |
</div> |
<!-- <pre class="code">{{ report | json }}</pre>--> |
</div><!--App--> |
</div> <!--form--> |
</div><!--Panel Body--> |
</div><!--Panel--> |
</div><!--container-fluid--> |
<% |
AbstractDao.getCurrentSession().getTransaction().commit(); |
%> |
/branches/grupo11/impl/src/web/user/utils/reportEdit.jsp |
---|
New file |
0,0 → 1,320 |
<%-- |
Created by IntelliJ IDEA. |
User: Pedro |
Date: 01/02/2018 |
Time: 18:05 |
To change this template use File | Settings | File Templates. |
--%> |
<%@ page contentType="text/html;charset=UTF-8" language="java" %> |
<link rel="stylesheet" href="<%=request.getContextPath()%>/js/jquery-ui-1.12.1/jquery-ui.css"> |
<script src="<%=request.getContextPath()%>/js/jquery-ui-1.12.1/jquery-ui.min.js"></script> |
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/flora-commons/flora.resizable.css"> |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.2/jspdf.min.js"></script> |
<script> |
function demoFromHTML() { |
var pdf = new jsPDF('p', 'pt', 'letter'); |
// source can be HTML-formatted string, or a reference |
// to an actual DOM element from which the text will be scraped. |
source = $('#courseReportApp')[0]; |
// we support special element handlers. Register them with jQuery-style |
// ID selector for either ID or node name. ("#iAmID", "div", "span" etc.) |
// There is no support for any other type of selectors |
// (class, of compound) at this time. |
specialElementHandlers = { |
// element with id of "bypass" - jQuery style selector |
'#bypassme': function (element, renderer) { |
// true = "handled elsewhere, bypass text extraction" |
return true |
} |
}; |
margins = { |
top: 80, |
bottom: 60, |
left: 40, |
width: 522 |
}; |
// all coords and widths are in jsPDF instance's declared units |
// 'inches' in this case |
pdf.fromHTML( |
source, // HTML string or DOM elem ref. |
margins.left, // x coord |
margins.top, { // y coord |
'width': margins.width, // max width of content on PDF |
'elementHandlers': specialElementHandlers |
}, |
function (dispose) { |
// dispose: object with X, Y of the last line add to the PDF |
// this allow the insertion of new lines after html |
//var pdfDocument = pdf.save('Test.pdf'); |
var pdfDocument = pdf.output(); |
var boundary = '---------------------------'; |
boundary += Math.floor(Math.random()*32768); |
boundary += Math.floor(Math.random()*32768); |
boundary += Math.floor(Math.random()*32768); |
var body = ''; |
body += '--' + boundary + '\r\n' + |
'Content-Disposition: form-data; name="filesInputId-UPLOAD[]"; filename="20170530_210340.pdf"' + '\r\n'; |
body += 'Content-Type: application/pdf'; |
body += '\r\n\r\n'; |
body += pdfDocument; |
body += '\r\n' |
body += '--' + boundary + '--'; |
$.ajax({ |
type: "POST", |
cache: false, |
url: "<%=request.getContextPath()%>/filesUpload", |
data: body , |
processData: false, |
contentType : 'multipart/form-data; boundary=' + boundary, |
success: function (data) { |
alert('success'); |
return false; |
} |
}); |
}, margins |
); |
} |
</script> |
<%--<a href="javascript:demoFromHTML()" class="button">Run Code</a>--%> |
<div class="container-fluid"> |
<style> |
.separatorSection |
{ |
border: 1px solid #ddd; |
} |
</style> |
<!-- Apresentacao da Unidade --> |
<div class="panel panel-default"> |
<div class="panel-heading"> |
Relatório Anual do curso: ${course.name} |
</div> |
<div class="panel-body"> |
<p><b class="label-info">Tipo de Curso:</b> <bean:message key="course.${course.degree}"/></p> |
<p><b class="label-info">Ano Lectivo:</b> ${course.importYear}</p> |
<p><b class="label-info">Departamento:</b> ${course.department.name}</p> |
<p><b class="label-info">Escola:</b> ${course.department.courseSchool.name}</p> |
<script> |
//Especifico da aplicacao |
var courseReportApp = angular.module('courseReportApp', ['ui.tree']); |
GLOBAL_BacoAngularAppDependencies.push('courseReportApp'); |
courseReportApp.directive('resizable', function () { |
return { |
restrict: 'A', |
scope: { |
callback: '&onResize' |
}, |
link: function postLink(scope, elem, attrs) { |
elem.resizable(); |
elem.on('resize', function (evt, ui, comp) { |
scope.$apply(function() { |
if (scope.callback) { |
scope.callback({$evt: evt, $ui: ui, $comp: comp }); |
} |
}) |
}); |
} |
}; |
}); |
courseReportApp.controller('courseReportAppController', function($scope) |
{ |
$scope.docAppSelector = "#courseReportApp"; |
$scope.report = ${courseDocumentJson}; |
$scope.resize = function(evt,ui,comp) { |
//console.log (evt,ui); |
comp.width = ui.size.width; |
comp.height = ui.size.height; |
} |
/** |
* @classe class to match |
* @superClasses array of strings |
* */ |
$scope.contains = function(obj,classe) |
{ |
if(obj['@class'] && obj['@class'] == classe) |
return true; |
if(obj.allSuperClasses) |
{ |
for(var i in obj.allSuperClasses) |
{ |
if(classe == obj.allSuperClasses[i]) |
return true; |
} |
} |
return false; |
} |
$scope.showSep = function(section,subSection) |
{ |
var s; |
for(s in section.sections) |
{ |
section.sections[s].active = false; |
} |
/*$(".separatorSectionNav").each(function() |
{ |
angular.element($(this)).scope().section.active = false; |
});*/ |
subSection.active = true; |
} |
/** |
* Este metodo devolve o template mais profundo na hierarquia de classes |
* permitindo emular o override, quanto mais especifica for a classe |
* e caso exista template é esse o template devolvido |
* procura um script com o id da classe e se nao existir |
* vai subindo nas super classes |
* @param obj |
* @returns {*} |
*/ |
$scope.class2id = function(obj) |
{ |
var objClassId = obj["@class"].replaceAll(".","_"); |
if($("script#" + objClassId).length > 0) |
{ |
return objClassId; |
} |
if(obj.allSuperClasses) |
{ |
var s; |
for(s in obj.allSuperClasses) |
{ |
var superClass = obj.allSuperClasses[s]; |
var superClassId = superClass.replaceAll(".","_"); |
if($("script#" + superClassId).length > 0) |
{ |
return superClassId; |
} |
} |
} |
return obj["@class"].replaceAll(".","_"); |
} |
$scope.addText = function(parentCustomPane) |
{ |
$scope.addSimpleDocComponent(parentCustomPane,"pt.estgp.estgweb.utils.documentBuilder.TextComponent") |
} |
$scope.addImage = function(parentCustomPane) |
{ |
$scope.addSimpleDocComponent(parentCustomPane,"pt.estgp.estgweb.utils.documentBuilder.ImageComponent") |
} |
$scope.addSimpleDocComponent = function(parentCustomPane,classComponent) |
{ |
if(!parentCustomPane.components) |
{ |
parentCustomPane.components = []; |
} |
parentCustomPane.components.push( |
{ |
"@class" : classComponent |
} |
); |
} |
$scope.removeComponent = function(index,array) |
{ |
array.splice(index,1); |
} |
$scope.callbackUploadedFiles = function(filesUploadResult,token,targetElement) |
{ |
var modelObject = BacoAngularUtils.getAngularElementModel(targetElement); |
if(modelObject.image && modelObject.image.identifier) |
{ |
widgetCallWithActionParameters( |
"<%=request.getContextPath()%>/user/json/repository.do", |
"replaceRepositoryFileFromTempPrivateDomain", |
{ |
"identifier" : modelObject.image.identifier, |
"fileUploaded" : BacoJS.stringifyOrdered(filesUploadResult.uploadedFiles[0]) |
}, |
"#courseReportApp", |
function(repositoryFile4JsonView) |
{ |
modelObject.image = repositoryFile4JsonView; |
//image URL is generated on reimport just to avoid caching |
modelObject.imageUrl = "<%=request.getContextPath()%>/repositoryStream/" + modelObject.image.identifier + "?" + new Date().getTime(); |
angular.element($("#courseReportApp")).scope().$apply(); |
}, |
function(){} |
); |
} |
else |
{ |
widgetCallWithActionParameters( |
"<%=request.getContextPath()%>/user/json/repository.do", |
"saveRepositoryFileFromTempPrivateDomain", |
{ |
"fileUploaded" : BacoJS.stringifyOrdered(filesUploadResult.uploadedFiles[0]) |
}, |
"#courseReportApp", |
function(repositoryFile4JsonView) |
{ |
modelObject.image = repositoryFile4JsonView; |
modelObject.imageUrl = "<%=request.getContextPath()%>/repositoryStream/" + modelObject.image.identifier + "?" + new Date().getTime(); |
angular.element($("#courseReportApp")).scope().$apply(); |
}, |
function(){} |
); |
} |
} |
}); |
</script> |
<!--TEMPLATES FOR DOCUMENT BUILDER--> |
<jsp:include page="../utils/documentsBuilder.jsp"/> |
<div class="form-vertical"> |
<div id="courseReportApp" ng-app="courseReportApp" ng-controller="courseReportAppController"> |
<div ng-init="section=report;" ng-include="'pt_estgp_estgweb_utils_documentBuilder_DocumentSection'"> |
</div> |
<!-- <pre class="code">{{ report | json }}</pre>--> |
</div><!--App--> |
</div> <!--form--> |
</div><!--Panel Body--> |
</div><!--Panel--> |
</div><!--container-fluid--> |
/branches/grupo11/impl/build.xml |
---|
66,6 → 66,7 |
<target name="initDirs"> |
<mkdir dir="${log.dir}"/> |
<mkdir dir="${data.dir}"/> |
<mkdir dir="${tmp.dir}"/> |
<mkdir dir="${build.dir}"/> |
<mkdir dir="${build.dir.classes}"/> |
<mkdir dir="${build.dir.war}"/> |
/branches/grupo11/impl/gen/java/pt/estgp/estgweb/domain/CourseYear.java |
---|
2,12 → 2,13 |
// Generated 4/nov/2017 19:35:42 by Hibernate Tools 3.2.0.b9 |
import java.io.Serializable; |
import java.util.Date; |
/** |
* CourseYear generated by hbm2java |
*/ |
public abstract class CourseYear extends pt.estgp.estgweb.domain.DomainSerializableObject implements java.io.Serializable { |
public abstract class CourseYear extends DomainSerializableObject implements Serializable { |
private long id; |
16,7 → 17,16 |
private String importYear; |
private boolean separatedTurmas; |
private Course course; |
private String courseReportDocument; |
public String getCourseReportDocument() { |
return courseReportDocument; |
} |
public void setCourseReportDocument(String courseReportDocument) { |
this.courseReportDocument = courseReportDocument; |
} |
public CourseYear() { |
} |