From: leo Date: Thu, 21 May 2009 06:48:49 +0000 (+0000) Subject: map bind X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=04995fc06691a832c7f33be25465b973bfd35a7d;p=xerial%2Fxerial-core.git map bind git-svn-id: http://www.xerial.org/svn/project/XerialJ/trunk/xerial-core@3327 ae02f08e-27ec-0310-ae8c-8ba02fe2eafd --- diff --git a/src/main/java/org/xerial/lens/MapEntry.java b/src/main/java/org/xerial/lens/MapEntry.java new file mode 100644 index 0000000..6ced2eb --- /dev/null +++ b/src/main/java/org/xerial/lens/MapEntry.java @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + * Copyright 2009 Taro L. Saito + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *--------------------------------------------------------------------------*/ +//-------------------------------------- +// XerialJ +// +// MapEntry.java +// Since: May 21, 2009 2:39:57 PM +// +// $URL$ +// $Author$ +//-------------------------------------- +package org.xerial.lens; + +public class MapEntry +{ + public Object key; + public Object value; +} diff --git a/src/main/java/org/xerial/lens/ObjectLens.java b/src/main/java/org/xerial/lens/ObjectLens.java index 9c0c8d6..42eaf77 100644 --- a/src/main/java/org/xerial/lens/ObjectLens.java +++ b/src/main/java/org/xerial/lens/ObjectLens.java @@ -42,6 +42,7 @@ import java.util.regex.Pattern; import org.xerial.json.JSONString; import org.xerial.json.JSONWriter; import org.xerial.util.Pair; +import org.xerial.util.bean.BeanUtil; import org.xerial.util.bean.TypeInfo; import org.xerial.util.log.Logger; import org.xerial.util.reflect.ReflectionUtil; @@ -96,7 +97,7 @@ public class ObjectLens return Collections.unmodifiableList(getterContainer); } - public boolean hasParameters() + public boolean hasAttributes() { return !getterContainer.isEmpty(); } @@ -171,7 +172,6 @@ public class ObjectLens // scan methods for (Method eachMethod : targetType.getMethods()) { - String methodName = eachMethod.getName(); String paramName = pickPropertyName(methodName); @@ -182,6 +182,10 @@ public class ObjectLens { case 1: { + Class< ? > parentOfTheSetter = eachMethod.getDeclaringClass(); + if (TypeInfo.isCollection(parentOfTheSetter) || TypeInfo.isMap(parentOfTheSetter)) + break; + addNewSetter(setterContainer, paramName, eachMethod); break; } @@ -192,8 +196,27 @@ public class ObjectLens if (relName == null) { // infer relation node names - relName = new Pair(getCanonicalParameterName(argTypes[0].getSimpleName()), - getCanonicalParameterName(argTypes[1].getSimpleName())); + if (TypeInfo.isMap(eachMethod.getDeclaringClass())) + { + + Class< ? >[] mapElementType = BeanUtil.resolveActualTypeOfMapElement(targetType, eachMethod + .getParameterTypes()); + + // map.put(Key, Value) + setterContainer + .add(ParameterSetter.newMapEntrySetter(mapElementType[0], mapElementType[1])); + + // (entry, key) + setterContainer.add(ParameterSetter.newKeySetter(mapElementType[0])); + // (entry, value) + setterContainer.add(ParameterSetter.newValueSetter(mapElementType[1])); + continue; + } + else + { + relName = new Pair(getCanonicalParameterName(argTypes[0].getSimpleName()), + getCanonicalParameterName(argTypes[1].getSimpleName())); + } } relationSetterContainer.add(RelationSetter.newRelationSetter(relName.getFirst(), relName @@ -224,7 +247,6 @@ public class ObjectLens } } - } private static void addNewSetter(List setterContainer, String paramPart, Method m) @@ -278,6 +300,9 @@ public class ObjectLens public static String getCanonicalParameterName(String paramName) { + if (paramName == null) + return paramName; + Matcher m = paramNameReplacePattern.matcher(paramName); return m.replaceAll("").toLowerCase(); } @@ -313,43 +338,48 @@ public class ObjectLens if (TypeInfo.isCollection(c)) { - if (lens.hasParameters()) + Collection< ? > collection = (Collection< ? >) obj; + boolean hasAttributes = lens.hasAttributes(); + + if (hasAttributes) { json.startObject(); outputParemters(json, obj); - json.startArray("entry"); + if (!collection.isEmpty()) + json.startArray("entry"); } - else + else if (!collection.isEmpty()) json.startArray(); - Collection< ? > collection = (Collection< ? >) obj; for (Object elem : collection) { toJSON(json, elem); } - json.endArray(); + if (!collection.isEmpty()) + json.endArray(); - if (lens.hasParameters()) + if (hasAttributes) json.endObject(); } else if (TypeInfo.isMap(c)) { - if (lens.hasParameters()) + Map< ? , ? > map = (Map< ? , ? >) obj; + boolean hasAttributes = lens.hasAttributes(); + + if (hasAttributes) { json.startObject(); outputParemters(json, obj); - json.startArray("entry"); + if (!map.isEmpty()) + json.startArray("entry"); } - else + else if (!map.isEmpty()) json.startArray(); - Map< ? , ? > map = (Map< ? , ? >) obj; - - json.startArray(); for (Entry< ? , ? > each : map.entrySet()) { json.startObject(); @@ -357,10 +387,11 @@ public class ObjectLens json.putObject("value", each.getValue()); json.endObject(); } - json.endArray(); - json.endArray(); - if (lens.hasParameters()) + if (!map.isEmpty()) + json.endArray(); + + if (hasAttributes) json.endObject(); } else diff --git a/src/main/java/org/xerial/lens/ObjectMapper.java b/src/main/java/org/xerial/lens/ObjectMapper.java index cd912fc..14428a5 100644 --- a/src/main/java/org/xerial/lens/ObjectMapper.java +++ b/src/main/java/org/xerial/lens/ObjectMapper.java @@ -33,6 +33,7 @@ import java.util.Set; import org.xerial.core.XerialError; import org.xerial.core.XerialErrorCode; import org.xerial.core.XerialException; +import org.xerial.lens.ParameterSetter.MapEntryBinder; import org.xerial.relation.Node; import org.xerial.relation.query.AmoebaJoinHandlerBase; import org.xerial.relation.query.QuerySet; @@ -199,7 +200,9 @@ public class ObjectMapper public void build(Class< ? > targetType, String alias) { - if (TypeInfo.isBasicType(targetType) || TypeInfo.isCollection(targetType)) + // TODO use context-based schema -> binder mapping + + if (TypeInfo.isBasicType(targetType) || TypeInfo.isCollection(targetType) || targetType == MapEntry.class) return; if (processedClasses.contains(targetType)) @@ -213,6 +216,17 @@ public class ObjectMapper for (ParameterSetter each : lens.getSetterList()) { + if (each.getClass() == MapEntryBinder.class) + { + SchemaBuilder builder = new SchemaBuilder(); + builder.add("entry"); + builder.add(each.getParameterName()); + Schema s = builder.build(); + qs.addQueryTarget(s); + schema2binder.put(s, new AttributeBinder(MapEntry.class, each)); + continue; + } + build(each.getParameterType(), each.getParameterName()); SchemaBuilder builder = new SchemaBuilder(); diff --git a/src/main/java/org/xerial/lens/ParameterSetter.java b/src/main/java/org/xerial/lens/ParameterSetter.java index 0a9259e..6db1dcb 100644 --- a/src/main/java/org/xerial/lens/ParameterSetter.java +++ b/src/main/java/org/xerial/lens/ParameterSetter.java @@ -26,8 +26,12 @@ package org.xerial.lens; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.Map; +import org.xerial.core.XerialError; +import org.xerial.core.XerialErrorCode; import org.xerial.core.XerialException; +import org.xerial.util.log.Logger; import org.xerial.util.reflect.ReflectionUtil; /** @@ -38,6 +42,8 @@ import org.xerial.util.reflect.ReflectionUtil; */ public abstract class ParameterSetter { + private static Logger _logger = Logger.getLogger(ParameterSetter.class); + private final Class< ? > parameterType; private final String parameterName; @@ -90,6 +96,21 @@ public abstract class ParameterSetter return new MethodSetter(parameterType, parameterName, setterMethod); } + public static ParameterSetter newKeySetter(Class< ? > keyType) + { + return new MapEntryBinder(keyType, "key"); + } + + public static ParameterSetter newValueSetter(Class< ? > valueType) + { + return new MapEntryBinder(valueType, "value"); + } + + public static ParameterSetter newMapEntrySetter(Class< ? > keyType, Class< ? > valueType) + { + return new MapEntrySetter(keyType, valueType); + } + private static class FieldSetter extends ParameterSetter { private final Field targetField; @@ -136,4 +157,76 @@ public abstract class ParameterSetter } + static class MapEntryBinder extends ParameterSetter + { + private final Field targetField; + + public MapEntryBinder(Class< ? > parameterType, String parameterName) + { + super(parameterType, parameterName); + + try + { + targetField = MapEntry.class.getField(parameterName); + } + catch (Exception e) + { + throw new XerialError(XerialErrorCode.INVALID_STATE, e); + } + + } + + @Override + public void bind(Object entry, Object key) throws XerialException + { + ReflectionUtil.setFieldValue(entry, targetField, key); + } + + } + + private static class MapEntrySetter extends ParameterSetter + { + final Class< ? > keyType; + final Class< ? > valueType; + + public MapEntrySetter(Class< ? > keyType, Class< ? > valueType) + { + super(MapEntry.class, "entry"); + + this.keyType = keyType; + this.valueType = valueType; + } + + @SuppressWarnings("unchecked") + @Override + public void bind(Object mapObject, Object entryObject) throws XerialException + { + Map map = Map.class.cast(mapObject); + if (map == null) + { + _logger.warn("not a map type: " + mapObject); + return; + } + + MapEntry entry = MapEntry.class.cast(entryObject); + if (entry == null) + { + _logger.warn("not a map entry type: " + entryObject); + return; + } + + try + { + if (entry.key != null) + map.put(keyType.cast(entry.key), valueType.cast(entry.value)); + } + catch (ClassCastException e) + { + _logger.warn("cannot convert type: " + e); + return; + } + } + + } + } diff --git a/src/main/java/org/xerial/lens/RelationSetter.java b/src/main/java/org/xerial/lens/RelationSetter.java index ff0266d..3d4912f 100644 --- a/src/main/java/org/xerial/lens/RelationSetter.java +++ b/src/main/java/org/xerial/lens/RelationSetter.java @@ -140,4 +140,5 @@ public abstract class RelationSetter } } + } diff --git a/src/main/java/org/xerial/util/bean/BeanUtil.java b/src/main/java/org/xerial/util/bean/BeanUtil.java index 603d1da..1bd00f0 100644 --- a/src/main/java/org/xerial/util/bean/BeanUtil.java +++ b/src/main/java/org/xerial/util/bean/BeanUtil.java @@ -450,7 +450,7 @@ public class BeanUtil return orig; } - private static Class< ? >[] resolveActualTypeOfMapElement(Type type, Class< ? >[] orig) + public static Class< ? >[] resolveActualTypeOfMapElement(Type type, Class< ? >[] orig) { ParameterizedType pt = getParentParameterizedType(type, Map.class); if (pt != null) diff --git a/src/test/java/org/xerial/lens/ObjectLensTest.java b/src/test/java/org/xerial/lens/ObjectLensTest.java index a849250..953e49d 100644 --- a/src/test/java/org/xerial/lens/ObjectLensTest.java +++ b/src/test/java/org/xerial/lens/ObjectLensTest.java @@ -26,6 +26,7 @@ package org.xerial.lens; import static org.junit.Assert.*; +import java.io.StringReader; import java.util.TreeMap; import org.junit.After; @@ -90,7 +91,24 @@ public class ObjectLensTest public static class ExtMap extends TreeMap { + /** + * + */ + private static final long serialVersionUID = 1L; + public String name = "ext-map"; + + @Override + public boolean equals(Object o) + { + if (!(o instanceof ExtMap)) + return false; + + ExtMap other = (ExtMap) o; + + return name.equals(other.name) && super.equals(other); + + } } @Test @@ -99,7 +117,13 @@ public class ObjectLensTest ExtMap extMap = new ExtMap(); extMap.put(1, "hello"); extMap.put(10, "world"); - _logger.info(ObjectLens.toJSON(extMap)); + String json = ObjectLens.toJSON(extMap); + _logger.debug(json); + + ExtMap extMap2 = Lens.loadJSON(ExtMap.class, new StringReader(json)); + _logger.debug(ObjectLens.toJSON(extMap2)); + + assertEquals(extMap, extMap2); } }