All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.toshiba.mwcloud.gs.RowKeyPredicate Maven / Gradle / Ivy

The newest version!
/*
   Copyright (c) 2017 TOSHIBA Digital Solutions Corporation

   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.toshiba.mwcloud.gs;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.toshiba.mwcloud.gs.common.GSErrorCode;
import com.toshiba.mwcloud.gs.common.RowMapper;

/**
 * Represents the condition that a {@link RowKey} satisfies.
 *
 * 

This is used as the search condition in {@link GridStore#multiGet(java.util.Map)}

* *

There are two types of conditions, range condition and individual condition. * The two types of conditions cannot be specified at the same time. * If the condition is not specified, it means that the condition is satisfied in all the target row keys.

* * @param type of {@link RowKey} * * @since 1.5 */ public class RowKeyPredicate { private static final RowMapper.Config KEY_MAPPER_CONFIG = new RowMapper.Config(true, true, true, true); private static final Map< GSType, Map> SINGLE_MAPPERS_BY_TYPE = makeSingleMapperMap(); private static final Map< Class, Map> SINGLE_MAPPERS_BY_CLASS = makeSingleMapperMap(SINGLE_MAPPERS_BY_TYPE); private static final Map, GSType> SINGLE_TYPE_MAP = makeSingleTypeMap(SINGLE_MAPPERS_BY_CLASS); private static boolean stringRangeRestricted = false; private final RowMapper.ValueDuplicator duplicator; private final boolean rangeAcceptable; private K start; private K finish; private Set distinctKeys; private RowKeyPredicate(RowMapper.ValueDuplicator duplicator) throws GSException { checkMapper(duplicator.getMapper()); this.duplicator = duplicator; this.rangeAcceptable = isRangeKeyAcceptable( duplicator.getValueClass(), duplicator.getMapper()); } /** * Creates an instance of {@link RowKeyPredicate} with the specified {@link GSType} as the {@link RowKey} type. * *

The target Container must have a {@link RowKey}, and the type of the {@link RowKey} must be * the specified {@link GSType}

* *

Also, the Column precision should match.

* *

Unlike {@link #create(Class)}, this method is used when the type of {@link RowKey} is not specified * when the application is compiled. However, the criteria for checking * the RowKey type and precision when setting the condition is the same.

* *

The type of {@link RowKey} that can be set is only that allowed * by either one of the subinterfaces of {@link Container}.

* * @param keyType type of {@link RowKey} used as a search condition * * @return {@link RowKeyPredicate} newly created * * @throws GSException if the specified type is not always supported as a {@link RowKey} * @throws NullPointerException if {@code null} is specified in the argument. * * @see Container */ public static RowKeyPredicate create(GSType keyType) throws GSException { return create(keyType, null); } /** * Creates a matching condition where the specified * {@link GSType} and date/time precision are the RowKey type. * *

The target Container for which a matching condition is evaluated * must have a RowKey, and the type of the RowKey must be the specified {@link GSType}.

* *

Also, the Column precision should match.

* *

Unlike {@link #create(Class)}, this method is appropriate for use when * the type of RowKey is not specified when the application is compiled. However, * the criteria for checking the RowKey type and precision when setting the condition is the same.

* *

The type of a RowKey that can be set is only the one that is * allowed by either one of the subinterfaces of {@link Container}.

* * @param keyType the type of a RowKey used to evaluate the matching condition. * @param timePrecision the date/time precision of a RowKey used to evaluate the matching condition. * * @return newly created {@link RowKeyPredicate} * * @throws GSException if the specified type is not supported as a RowKey at any time. * @throws NullPointerException If {@code null}が is specified as a {@code keyType} argument * * @see Container * @since 5.3 */ public static RowKeyPredicate create( GSType keyType, TimeUnit timePrecision) throws GSException { final RowMapper mapper = findSingleMapper(keyType, timePrecision); if (mapper == null) { GSErrorCode.checkNullParameter(keyType, "keyType", null); throw new GSException(GSErrorCode.UNSUPPORTED_KEY_TYPE, "Unsupported key type (type=" + keyType + ")"); } return new RowKeyPredicate( RowMapper.ValueDuplicator.createSingle(Object.class, mapper)); } /** * Creates an instance of {@link RowKeyPredicate} with the {@link GSType} corresponding to * the specified {@link Class} as the {@link RowKey} type. * *

The container to be evaluated by the search condition must have a Row key * with a single column, and the type of the Row key must be same as the specified * {@link GSType}.

* *

Also, the Column precision should match.

* *

The type and precision of a RowKey that can be set are only * those that are allowed by either one of the subinterfaces of {@link Container}. * For the correspondence of {@link Class} to {@link GSType}, * see the definition of {@link Container}.

* *

For a composite Row key, use {@link #create(ContainerInfo)} which allows to * create a search condition regardless of the number of columns that configure * a Row key.

* * @param keyType {@link Class} corresponding to a {@link RowKey} used as a search condition * * @return {@link RowKeyPredicate} newly created * * @throws GSException if the specified type is not always supported as a {@link RowKey} * @throws NullPointerException if {@code null} is specified in the argument. * * @see Container */ public static RowKeyPredicate create(Class keyType) throws GSException { return create(keyType, null); } /** * Creates a matching condition where the {@link GSType} corresponding to * the specified {@link Class} and date/time precision is the RowKey type. * *

The target Container for which a matching condition is evaluated * must have a RowKey composed of a single column, and the type of the * RowKey must be the same as the type of the specified {@link GSType}.

* *

Also, the Column precision should match.

* *

The type of a RowKey that can be set is only the one that is * allowed by either one of the subinterfaces of {@link Container} * For more about the mapping between {@link Class} and {@link GSType}, * see the definition of a {@link Container}.

* *

Use {@link #create(ContainerInfo)} to create a matching condition regardless of * the number of columns configuring a RowKey such as a composite Row key.

* * @param keyType the {@link Class} that corresponds to the type of a RowKey used to evaluate the matching condition. * * @return newly created {@link RowKeyPredicate} * * @throws GSException if the specified type is not supported as a RowKey at any time. * @throws NullPointerException If {@code null} is specified as a {@code keyType} argument * * @see Container * @since 5.3 */ public static RowKeyPredicate create( Class keyType, TimeUnit timePrecision) throws GSException { final RowMapper mapper = findSingleMapper(keyType, timePrecision); if (mapper == null) { GSErrorCode.checkNullParameter(keyType, "keyType", null); throw new GSException(GSErrorCode.UNSUPPORTED_KEY_TYPE, "Unsupported key type (type=" + keyType + ")"); } return new RowKeyPredicate( RowMapper.ValueDuplicator.createSingle(keyType, mapper)); } /** * Creates an instance of {@link RowKeyPredicate} based on the Row key column * definition described in the specified {@link ContainerInfo}. * *

The container to be evaluated by the search condition must have a Row key * and must correspond to the column definition described in the specified * {@link ContainerInfo}. Column definitions other than that of the Row key are * not used for the evaluation.

* * @param info Container information including the column layout of the Row key * for which the search condition is determined. Other contents are ignored * * @return {@link RowKeyPredicate} newly created * * @throws GSException if the specified information does not include the Row key * or is always not supported as a Row key * @throws NullPointerException if {@code null} is specified in the argument. * * @since 4.3 */ public static RowKeyPredicate create( ContainerInfo info) throws GSException { GSErrorCode.checkNullParameter(info, "info", null); final RowMapper mapper = RowMapper.getInstance(info.getType(), info, KEY_MAPPER_CONFIG); return new RowKeyPredicate( RowMapper.ValueDuplicator.createForKey(mapper)); } /** * Sets the value of the {@link RowKey} at the starting position of the range condition. * *

A {@link RowKey} with a value smaller than the specified value is deemed as non-conforming.

* *

A Row key with no magnitude relation defined, including a Row key of the * STRING type or one with a composite Row key containing STRING type, are not * used for the determination even though it can be set as a condition.

* * @param startKey value of {@link RowKey} at the starting position or {@code null}. * For {@code null}, the setting is cancelled. * * @throws GSException if an individual condition had been set already * @throws ClassCastException the specified RowKey is not NULL or the type is not supported as {@link RowKey} */ public void setStart(K startKey) throws GSException { if (distinctKeys != null) { throw new GSException(GSErrorCode.ILLEGAL_PARAMETER, "Distinct key has already been specified"); } checkRangeKey(startKey); final K checkedKey = checkKeyType(startKey); start = duplicateKey(checkedKey, false); } /** * Sets the value of the {@link RowKey} at the last position of the range condition. * *

A {@link RowKey} with a value larger than the specified value is deemed as non-conforming.

* *

A Row key with no magnitude relation defined, including a Row key of the * STRING type or one with a composite Row key containing STRING type, are not * used for the determination even though it can be set as a condition.

* * @param finishKey the value of {@link RowKey} at the last position or {@code null}. * For {@code null}, the setting is cancelled. * * @throws GSException if an individual condition had been set already * @throws ClassCastException the value of specified key is not NULL * or the type is not supported as {@link RowKey} */ public void setFinish(K finishKey) throws GSException { if (distinctKeys != null) { throw new GSException(GSErrorCode.ILLEGAL_PARAMETER, "Distinct key has already been specified"); } checkRangeKey(finishKey); final K checkedKey = checkKeyType(finishKey); finish = duplicateKey(checkedKey, false); } /** * Appends the value of the {@link RowKey} as one of the elements of the individual condition. * *

A {@link RowKey} with the same value as the added value is deemed as conforming.

* * @param key value of {@link RowKey} to be appended as one of the elements * of the individual condition. Must not be a {@code null} value. * * @throws GSException if a range condition had already been set * @throws ClassCastException the value of the specified key is not NULL or the type is not supported as {@link RowKey} * @throws NullPointerException when {@code null} is specified as an argument */ public void add(K key) throws GSException { if (start != null || finish != null) { throw new GSException(GSErrorCode.ILLEGAL_PARAMETER, "Start or finish key has already been specified"); } GSErrorCode.checkNullParameter(key, "key", null); final K checkedKey = checkKeyType(key); Set distinctKeys = this.distinctKeys; if (distinctKeys == null) { distinctKeys = new HashSet(); } distinctKeys.add(duplicateKey(checkedKey, true)); this.distinctKeys = distinctKeys; } /** * Returns the type of Row key used as a search condition, except when * it is a search condition for the composite Row key. * *

Uses {@link #getKeySchema()} to get the schema for any Row key including * the composite Row key.

* * @return the type of {@link RowKey} used as a search condition. * * @throws IllegalStateException if called for a search condition for a * composite Row key. */ public GSType getKeyType() { if (getRowMapper().getKeyCategory() != RowMapper.KeyCategory.SINGLE) { throw new IllegalStateException( "This method cannot be used for composite row key"); } final GSType keyType = SINGLE_TYPE_MAP.get(getKeyClass()); if (keyType == null) { throw new Error(); } return keyType; } /** * Returns the Row key schema used as a search condition. * *

The schema information to be returned does not have column information other * than the Row key and container information other than the schema, even though it * is in the information used to create the search condition.

* * @return {@link ContainerInfo} that has only container information related to * the schema of the Row key * * @since 4.3 */ public ContainerInfo getKeySchema() { try { return getRowMapper().resolveKeyContainerInfo(); } catch (GSException e) { throw new Error(e); } } /** * Returns the value of the {@link RowKey} at the starting position of the range condition. * * @return the value of {@link RowKey} at the starting position * of the range condition, or {@code null} if it is not set. */ public K getStart() { return duplicateKey(start, false); } /** * Returns the value of {@link RowKey} at the last position * of the range condition. * * @return the value of {@link RowKey} at the last position * of the range condition, or {@code null} if it is not set. */ public K getFinish() { return duplicateKey(finish, false); } /** * Returns a Collection containing all of the values of the row keys * that make up the individual condition. * *

It is not defined whether an exception like {@link UnsupportedOperationException} * will occur during execution, when a returned object is updated. * Moreover, after an object is returned, it is not defined * whether an update of this object will change the contents of the returned object.

* * @return {@link java.util.Collection} containing all of the values of the row keys * that make up the individual condition. */ public java.util.Collection getDistinctKeys() { if (distinctKeys == null) { return null; } final java.util.Collection baseKeys; if (duplicator.isImmutable()) { baseKeys = distinctKeys; } else { final List keys = new ArrayList(distinctKeys.size()); for (K key : distinctKeys) { keys.add(duplicateKey(key, false)); } baseKeys = keys; } return Collections.unmodifiableCollection(baseKeys); } private Class getBindingClass() { return duplicator.getBindingClass(); } private Class getKeyClass() { return duplicator.getValueClass(); } private RowMapper getRowMapper() { return duplicator.getMapper(); } private K duplicateKey(K src, boolean identical) { if (src == null) { return null; } return duplicator.duplicate(src, identical); } private void checkRangeKey(Object obj) throws GSException { if (obj == null) { return; } if (!rangeAcceptable) { throw new GSException(GSErrorCode.UNSUPPORTED_OPERATION, "String range is not supported"); } } private K checkKeyType(K obj) throws GSException { if (obj == null) { return null; } final Class bindingClass = getBindingClass(); if (bindingClass == Object.class) { if (obj instanceof Row.Key) { final Row.Key generalKey = (Row.Key) obj; final Object elemObj = generalKey.getValue(0); checkKeyType(elemObj, generalKey); return bindingClass.cast(elemObj); } } else if (bindingClass == Row.Key.class) { checkKeyType(obj, obj); return obj; } checkKeyType(obj, null); return obj; } private void checkKeyType(Object obj, Object generalObj) throws GSException { final Class keyClass = getKeyClass(); try { keyClass.cast(obj); } catch (ClassCastException cause) { final ClassCastException e = new ClassCastException("Row key class unmatched"); e.initCause(cause); throw e; } if (generalObj != null) { final RowMapper objMapper; try { objMapper = RowMapper.getInstance( (Row) generalObj, KEY_MAPPER_CONFIG); } catch (GSException e) { throw new IllegalArgumentException(e); } try { objMapper.checkKeySchemaMatched(getRowMapper()); } catch (GSException e) { throw new GSException( "Row key schema unmatched", e); } } } private static RowMapper findSingleMapper( GSType keyType, TimeUnit timePrecision) throws GSException { final Map subMap = SINGLE_MAPPERS_BY_TYPE.get(keyType); if (subMap == null) { return null; } final int precisionLevel = RowMapper.TypeUtils.resolvePrecisionLevel( keyType, timePrecision); return subMap.get(precisionLevel); } private static RowMapper findSingleMapper( Class keyClass, TimeUnit timePrecision) throws GSException { final Map subMap = SINGLE_MAPPERS_BY_CLASS.get(keyClass); if (subMap == null) { return null; } final int precisionLevel = RowMapper.TypeUtils.resolvePrecisionLevel( keyClass, timePrecision); return subMap.get(precisionLevel); } private static Map> makeSingleMapperMap() { final Map> map = new EnumMap>(GSType.class); for (GSType type : RowMapper.TypeUtils.getKeyTypeSet()) { final Map subMap = new HashMap(); for (int precisionLevel : RowMapper.TypeUtils.getPrecisionLevelSet(type)) { final ColumnInfo.Builder builder = new ColumnInfo.Builder(); builder.setType(type); builder.setTimePrecision( RowMapper.TypeUtils.resolveTimePrecision( type, precisionLevel)); final boolean rowKeyAssigned = true; final ContainerInfo info = new ContainerInfo( null, null, Collections.singletonList(builder.toInfo()), rowKeyAssigned); final RowMapper mapper; try { mapper = RowMapper.getInstance( null, info, KEY_MAPPER_CONFIG); } catch (GSException e) { throw new Error(e); } subMap.put(precisionLevel, mapper); } map.put(type, subMap); } return map; } private static Map, Map> makeSingleMapperMap( Map> src) { final Map, Map> map = new HashMap, Map>(); for (GSType type : src.keySet()) { final Map srcSubMap = src.get(type); for (int precisionLevel : srcSubMap.keySet()) { final RowMapper mapper = srcSubMap.get(precisionLevel); final Class keyClass = RowMapper.ValueDuplicator.createSingle( Object.class, mapper).getValueClass(); Map subMap = map.get(keyClass); if (subMap == null) { subMap = new HashMap(); map.put(keyClass, subMap); } subMap.put(precisionLevel, mapper); } } return map; } private static Map, GSType> makeSingleTypeMap( Map, Map> src) { final Map, GSType> map = new HashMap, GSType>(); for (Class keyClass : src.keySet()) { final Map srcSubMap = src.get(keyClass); final RowMapper mapper = srcSubMap.values().iterator().next(); final GSType type = mapper.getContainerInfo().getColumnInfo(0).getType(); map.put(keyClass, type); } return map; } private static void checkMapper(RowMapper mapper) throws GSException { if (!mapper.hasKey()) { throw new GSException( GSErrorCode.KEY_NOT_FOUND, "Row key does not exist on predicate for row key"); } } private static boolean isRangeKeyAcceptable( Class keyClass, RowMapper mapper) throws GSException { if (!stringRangeRestricted) { return true; } if (keyClass != Row.Key.class) { return (keyClass != String.class); } final ContainerInfo info = mapper.resolveKeyContainerInfo(); final int columnCount = info.getColumnCount(); for (int i = 0; i < columnCount; i++) { if (info.getColumnInfo(i).getType() == GSType.STRING) { return false; } } return true; } }