/*
* This file is provided to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.basho.riak.client.convert.reflect;
import static com.basho.riak.client.convert.reflect.ClassUtil.getFieldValue;
import static com.basho.riak.client.convert.reflect.ClassUtil.setFieldValue;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.basho.riak.client.RiakLink;
import com.basho.riak.client.convert.UsermetaField;
import com.basho.riak.client.query.indexes.RiakIndexes;
/**
* Class that contains the Riak annotated fields for an annotated class
*
* @author russell
*
*/
public class AnnotationInfo {
private static final String NO_RIAK_KEY_FIELD_PRESENT = "no riak key field present";
private final Field riakKeyField;
private final List<UsermetaField> usermetaItemFields;
private final Field usermetaMapField;
private final List<RiakIndexField> indexFields;
private final Field riakLinksField;
/**
* @param riakKeyField
* @param usermetaItemFields
* @param usermetaMapField
*/
public AnnotationInfo(Field riakKeyField, List<UsermetaField> usermetaItemFields, Field usermetaMapField, List<RiakIndexField> indexFields, Field riakLinksField) {
this.riakKeyField = riakKeyField;
this.usermetaItemFields = usermetaItemFields;
validateUsermetaMapField(usermetaMapField);
this.usermetaMapField = usermetaMapField;
this.indexFields = indexFields;
validateRiakLinksField(riakLinksField);
this.riakLinksField = riakLinksField;
}
/**
* @param riakLinksField
*/
private void validateRiakLinksField(Field riakLinksField) {
if (riakLinksField == null) {
return;
}
ParameterizedType type = (ParameterizedType) riakLinksField.getGenericType();
if (type.getRawType().equals(Collection.class)) {
Type[] genericParams = type.getActualTypeArguments();
if (genericParams.length == 1 && genericParams[0].equals(RiakLink.class)) {
return;
}
}
throw new IllegalArgumentException("riak links field must be Collection<RiakLink>");
}
/**
* @param usermetaMapField
*/
private void validateUsermetaMapField(Field usermetaMapField) {
if (usermetaMapField == null) {
return;
}
ParameterizedType type = (ParameterizedType) usermetaMapField.getGenericType();
if (type.getRawType().equals(Map.class)) {
Type[] genericParams = type.getActualTypeArguments();
if (genericParams.length == 2 && genericParams[0].equals(String.class) &&
genericParams[1].equals(String.class)) {
return;
}
}
throw new IllegalArgumentException("user meta map field must be Map<String, String>");
}
/**
* @return
*/
public boolean hasRiakKey() {
return riakKeyField != null;
}
/**
* @param <T>
* @param obj
* @return
*/
public <T> String getRiakKey(T obj) {
if (!hasRiakKey()) {
throw new IllegalStateException(NO_RIAK_KEY_FIELD_PRESENT);
}
Object key = getFieldValue(riakKeyField, obj);
return key == null ? null : key.toString();
}
public <T> void setRiakKey(T obj, String key) {
if (!hasRiakKey()) {
throw new IllegalStateException(NO_RIAK_KEY_FIELD_PRESENT);
}
setFieldValue(riakKeyField, obj, key);
}
@SuppressWarnings({ "unchecked", "rawtypes" }) public <T> Map<String, String> getUsermetaData(T obj) {
final Map<String, String> usermetaData = new LinkedHashMap<String, String>();
Map<String, String> objectMetaMap = null;
for (UsermetaField f : usermetaItemFields) {
Object o = getFieldValue(f.getField(), obj);
String val = o == null ? null : o.toString();
String key = f.getUsermetaDataKey();
// null is not a user meta datum
if(o != null) {
usermetaData.put(key, val);
}
}
if (usermetaMapField != null) {
objectMetaMap = (Map) getFieldValue(usermetaMapField, obj);
}
if (objectMetaMap != null) {
usermetaData.putAll(objectMetaMap);
}
return usermetaData;
}
public <T> void setUsermetaData(final Map<String, String> usermetaData, T obj) {
// copy as we will modify
final Map<String, String> localMetaCopy = new HashMap<String, String>(usermetaData);
// set any individual annotated fields
for (UsermetaField f : usermetaItemFields) {
if (localMetaCopy.containsKey(f.getUsermetaDataKey())) {
setFieldValue(f.getField(), obj, localMetaCopy.get(f.getUsermetaDataKey()));
localMetaCopy.remove(f.getUsermetaDataKey());
}
}
// set a catch all map field
if(usermetaMapField != null) {
setFieldValue(usermetaMapField, obj, localMetaCopy);
}
}
/**
* @return a {@link RiakIndexes} made of the values of the RiakIndex
* annotated fields
*/
public <T> RiakIndexes getIndexes(T obj) {
final RiakIndexes riakIndexes = new RiakIndexes();
for (RiakIndexField f : indexFields) {
Object val = getFieldValue(f.getField(), obj);
// null is not an index value
if (val != null) {
if (val instanceof String) {
riakIndexes.add(f.getIndexName(), (String) val);
} else {
riakIndexes.add(f.getIndexName(), (Integer) val);
}
}
}
return riakIndexes;
}
/**
* TODO handle multi-value indexes with the same name
*
* @param <T>
* @param indexes
* the RiakIndexes to copy to the domain object
* @param obj
* the domain object to set indexes on
*/
public <T> void setIndexes(RiakIndexes indexes, T obj) {
// copy the index values to the correct fields
for (RiakIndexField f : indexFields) {
Set<?> val = null;
if (Integer.class.equals(f.getType()) || int.class.equals(f.getType())) {
val = indexes.getIntIndex(f.getIndexName());
}
if (String.class.equals(f.getType())) {
val = indexes.getBinIndex(f.getIndexName());
}
if (val != null && !val.isEmpty()) {
setFieldValue(f.getField(), obj, val.iterator().next()); // take the first value
}
}
}
@SuppressWarnings("unchecked") public <T> Collection<RiakLink> getLinks(T obj) {
final Collection<RiakLink> links = new ArrayList<RiakLink>();
if (riakLinksField != null) {
Object o = getFieldValue(riakLinksField, obj);
if (o != null && o instanceof Collection) {
links.addAll((Collection<RiakLink>) o);
}
}
return links;
}
public <T> void setLinks(Collection<RiakLink> links, T obj) {
if (riakLinksField != null) {
setFieldValue(riakLinksField, obj, links);
}
}
}