/*
Copyright (c) 2014 LinkedIn Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.linkedin.restli.internal.common;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.restli.common.ComplexKeySpec;
import com.linkedin.restli.common.CompoundKey;
import com.linkedin.restli.common.ResourceMethod;
import com.linkedin.restli.common.ResourceProperties;
import com.linkedin.restli.common.TypeSpec;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Runtime representation of resource spec.
*/
public class ResourcePropertiesImpl implements ResourceProperties
{
private final Set<ResourceMethod> _supportedMethods;
/*
* _keyType is a multi-purpose field. If it's null, the resource is either an actionSet or a simple resource. If it is non-null
* it contains either a primitive type (for collections with a primitive key type), or it is ComplexResourceKey.class for
* complex key collect resources or it is CompoundKey.class for associations.
*/
private final TypeSpec<?> _keyType;
private final ComplexKeySpec<? extends RecordTemplate, ? extends RecordTemplate> _complexKeyType; // present for complex key collections
private final Map<String, CompoundKey.TypeInfo> _keyParts; // present for associations
private final TypeSpec<? extends RecordTemplate> _valueType;
/**
* Initialize a ResourcePropertiesImpl with the given data.
*
* @param supportedMethods Set of ResourceMethods supported
* @param key type of the key of the Resource, may be either a typeref or a primitive
* @param complexKeyType the key, if the key is a ComplexResourceKey, otherwise null
* @param value the type of the RecordTemplate that the Resource manages
* @param keyParts Map of key names to key types (AssocKeyBindingTypes
* or, for backward compatibility, Class<?>), if the keyClass is a {@link CompoundKey}.
*/
public ResourcePropertiesImpl(Set<ResourceMethod> supportedMethods,
TypeSpec<?> key,
ComplexKeySpec<?, ?> complexKeyType,
TypeSpec<? extends RecordTemplate> value,
Map<String, ?> keyParts)
{
_supportedMethods = Collections.unmodifiableSet(supportedMethods);
_keyType = key;
_complexKeyType = complexKeyType;
_keyParts = Collections.unmodifiableMap(toTypeInfoKeyParts(keyParts));
_valueType = value;
}
@Override
public Set<ResourceMethod> getSupportedMethods()
{
return _supportedMethods;
}
@Override
public TypeSpec<?> getKeyType()
{
return _keyType;
}
@Override
public Map<String, CompoundKey.TypeInfo> getKeyParts()
{
return _keyParts;
}
@Override
public TypeSpec<? extends RecordTemplate> getValueType()
{
return _valueType;
}
@Override
public ComplexKeySpec<? extends RecordTemplate, ? extends RecordTemplate> getComplexKeyType()
{
return _complexKeyType;
}
@Override
public boolean isKeylessResource()
{
return _keyType == null;
}
@Override
public boolean equals(Object other)
{
if (this == other)
return true;
if (!(other instanceof ResourcePropertiesImpl))
return false;
ResourcePropertiesImpl resourceSpec = (ResourcePropertiesImpl)other;
return fieldEquals(_supportedMethods, resourceSpec._supportedMethods)
&& fieldEquals(_keyType, resourceSpec._keyType)
&& fieldEquals(_complexKeyType, resourceSpec._complexKeyType)
&& fieldEquals(_valueType, resourceSpec._valueType)
&& fieldEquals(_keyParts, resourceSpec._keyParts);
}
private boolean fieldEquals(Object field1, Object field2)
{
return field1 == null ? field2 == null : field1.equals(field2);
}
@Override
public int hashCode()
{
int res = 7;
res = 11 * res + hashOrNull(_supportedMethods);
res = 11 * res + hashOrNull(_keyType);
res = 11 * res + hashOrNull(_complexKeyType);
res = 11 * res + hashOrNull(_valueType);
res = 11 * res + hashOrNull(_keyParts);
return res;
}
private int hashOrNull(Object o)
{
return o == null ? 0 : o.hashCode();
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("supported: ");
builder.append(_supportedMethods);
builder.append("\n, key: ");
builder.append(_keyType);
builder.append("\n, complexKey: ");
builder.append(_complexKeyType);
builder.append("\n, value: ");
builder.append(_valueType);
builder.append("\n, keyParts: ");
builder.append(_keyParts);
return builder.toString();
}
private static HashMap<String, CompoundKey.TypeInfo> toTypeInfoKeyParts(Map<String, ?> keyParts)
{
final HashMap<String, CompoundKey.TypeInfo> keyPartTypeInfos = new HashMap<String, CompoundKey.TypeInfo>();
for(Map.Entry<String, ?> entry : keyParts.entrySet()) {
if(entry.getValue() instanceof Class<?>)
{
final Class<?> entryKeyClass = (Class<?>) entry.getValue();
keyPartTypeInfos.put(entry.getKey(), new CompoundKey.TypeInfo(entryKeyClass, entryKeyClass));
}
else if (entry.getValue() instanceof CompoundKey.TypeInfo)
{
keyPartTypeInfos.put(entry.getKey(), (CompoundKey.TypeInfo) entry.getValue());
}
else
{
throw new IllegalArgumentException("keyParts values must be either Class<?> or CompoundKey.TypeInfo, but was: " + entry.getValue().getClass());
}
}
return keyPartTypeInfos;
}
}