Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.cinchapi.concourse.util.Convert Maven / Gradle / Ivy
/*
* Copyright (c) 2013-2018 Cinchapi Inc.
*
* 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.cinchapi.concourse.util;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;
import javax.annotation.concurrent.Immutable;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import com.cinchapi.ccl.util.NaturalLanguage;
import com.cinchapi.common.base.AnyStrings;
import com.cinchapi.common.base.CheckedExceptions;
import com.cinchapi.concourse.Concourse;
import com.cinchapi.concourse.Link;
import com.cinchapi.concourse.Tag;
import com.cinchapi.concourse.Timestamp;
import com.cinchapi.concourse.annotate.PackagePrivate;
import com.cinchapi.concourse.annotate.UtilityClass;
import com.cinchapi.concourse.thrift.Operator;
import com.cinchapi.concourse.thrift.TObject;
import com.cinchapi.concourse.thrift.Type;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.primitives.Longs;
import com.google.gson.JsonParseException;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
/**
* A collection of functions to convert objects. The public API defined in
* {@link Concourse} uses certain objects for convenience that are not
* recognized by Thrift, so it is necessary to convert back and forth between
* different representations.
*
* @author Jeff Nelson
*/
@UtilityClass
public final class Convert {
/**
* A mapping from strings that can be translated to {@link Operator
* operators} to the operations to which they can be translated.
*/
@PackagePrivate
static Map OPERATOR_STRINGS;
static {
OPERATOR_STRINGS = Maps.newHashMap();
OPERATOR_STRINGS.put("==", Operator.EQUALS);
OPERATOR_STRINGS.put("=", Operator.EQUALS);
OPERATOR_STRINGS.put("eq", Operator.EQUALS);
OPERATOR_STRINGS.put("!=", Operator.NOT_EQUALS);
OPERATOR_STRINGS.put("ne", Operator.NOT_EQUALS);
OPERATOR_STRINGS.put(">", Operator.GREATER_THAN);
OPERATOR_STRINGS.put("gt", Operator.GREATER_THAN);
OPERATOR_STRINGS.put(">=", Operator.GREATER_THAN_OR_EQUALS);
OPERATOR_STRINGS.put("gte", Operator.GREATER_THAN_OR_EQUALS);
OPERATOR_STRINGS.put("<", Operator.LESS_THAN);
OPERATOR_STRINGS.put("lt", Operator.LESS_THAN);
OPERATOR_STRINGS.put("<=", Operator.LESS_THAN_OR_EQUALS);
OPERATOR_STRINGS.put("lte", Operator.LESS_THAN_OR_EQUALS);
OPERATOR_STRINGS.put("><", Operator.BETWEEN);
OPERATOR_STRINGS.put("bw", Operator.BETWEEN);
OPERATOR_STRINGS.put("->", Operator.LINKS_TO);
OPERATOR_STRINGS.put("lnks2", Operator.LINKS_TO);
OPERATOR_STRINGS.put("lnk2", Operator.LINKS_TO);
OPERATOR_STRINGS.put("regex", Operator.REGEX);
OPERATOR_STRINGS.put("nregex", Operator.NOT_REGEX);
OPERATOR_STRINGS.put("like", Operator.LIKE);
OPERATOR_STRINGS.put("nlike", Operator.NOT_LIKE);
for (Operator operator : Operator.values()) {
OPERATOR_STRINGS.put(operator.name(), operator);
OPERATOR_STRINGS.put(operator.symbol(), operator);
}
OPERATOR_STRINGS = ImmutableMap.copyOf(OPERATOR_STRINGS);
}
/**
* The component of a resolvable link symbol that comes after the
* resolvable key specification in the raw data.
*/
@PackagePrivate
static final String RAW_RESOLVABLE_LINK_SYMBOL_APPEND = "@"; // visible
// for
// testing
/**
* The component of a resolvable link symbol that comes before the
* resolvable key specification in the raw data.
*/
@PackagePrivate
static final String RAW_RESOLVABLE_LINK_SYMBOL_PREPEND = "@"; // visible
// for
// testing
/**
* These classes have a special encoding that signals that string value
* should actually be converted to those instances in
* {@link #jsonToJava(JsonReader)}.
*/
private static Set> CLASSES_WITH_ENCODED_STRING_REPR = Sets
.newHashSet(Link.class, Tag.class, ResolvableLink.class,
Timestamp.class);
/**
* A {@link Pattern} that can be used to determine whether a string matches
* the expected pattern of an instruction to insert links to records that
* are resolved by finding matches to a criteria.
*/
// NOTE: This REGEX enforces that the string must contain at least one
// space, which means that a CCL string can only be considered valid if it
// contains a space (e.g. name=jeff is not valid CCL).
private static final Pattern STRING_RESOLVABLE_LINK_REGEX = Pattern
.compile("^@(?=.*[ ]).+@$");
/**
* The character that indicates a String should be treated as a {@link Tag}.
*/
private static final char TAG_MARKER = '`';
/**
* Takes a JSON string representation of an object or an array of JSON
* objects and returns a list of {@link Multimap multimaps} with the
* corresponding data. Unlike {@link #jsonToJava(String)}, this method will
* allow the top level element to be an array in the {code json} string.
*
* @param json
* @return A list of Java objects
*/
public static List> anyJsonToJava(String json) {
List> result = Lists.newArrayList();
try (JsonReader reader = new JsonReader(new StringReader(json))) {
reader.setLenient(true);
if(reader.peek() == JsonToken.BEGIN_ARRAY) {
try {
reader.beginArray();
while (reader.peek() != JsonToken.END_ARRAY) {
result.add(jsonToJava(reader));
}
reader.endArray();
}
catch (IllegalStateException e) {
throw new JsonParseException(e.getMessage());
}
}
else {
result.add(jsonToJava(reader));
}
}
catch (IOException e) {
throw CheckedExceptions.wrapAsRuntimeException(e);
}
return result;
}
/**
* Return a List that represents the Thrift representation of each of the
* {@code objects} in the input list.
*
* @param objects a List of java objects
* @return a List of TObjects
*/
public static List javaListToThrift(List objects) {
List thrift = Lists.newArrayListWithCapacity(objects.size());
javaCollectionToThrift(objects, thrift);
return thrift;
}
/**
* Return a Map that represents the Thrift representation of each of the
* {@code objects} in the input Map.
*
* @param objects a Map of java objects
* @return a Map of TObjects
*/
public static Map javaMapToThrift(Map objects) {
Map thrift = Maps.newLinkedHashMap();
for (Entry entry : objects.entrySet()) {
K key = entry.getKey();
TObject value = javaToThrift(entry.getValue());
thrift.put(key, value);
}
return thrift;
}
/**
* Return a Set that represents the Thrift representation of each of the
* {@code objects} in the input Set.
*
* @param objects a Set of java objects
* @return a Set of TObjects
*/
public static Set javaSetToThrift(Set objects) {
Set thrift = Sets
.newLinkedHashSetWithExpectedSize(objects.size());
javaCollectionToThrift(objects, thrift);
return thrift;
}
/**
* Return the Thrift Object that represents {@code object}.
*
* @param object
* @return the TObject
*/
public static TObject javaToThrift(Object object) {
if(object == null) {
return TObject.NULL;
}
else {
ByteBuffer bytes;
Type type = null;
if(object instanceof Boolean) {
bytes = ByteBuffer.allocate(1);
bytes.put((boolean) object ? (byte) 1 : (byte) 0);
type = Type.BOOLEAN;
}
else if(object instanceof Double) {
bytes = ByteBuffer.allocate(8);
bytes.putDouble((double) object);
type = Type.DOUBLE;
}
else if(object instanceof Float) {
bytes = ByteBuffer.allocate(4);
bytes.putFloat((float) object);
type = Type.FLOAT;
}
else if(object instanceof Link) {
bytes = ByteBuffer.allocate(8);
bytes.putLong(((Link) object).longValue());
type = Type.LINK;
}
else if(object instanceof Long) {
bytes = ByteBuffer.allocate(8);
bytes.putLong((long) object);
type = Type.LONG;
}
else if(object instanceof Integer) {
bytes = ByteBuffer.allocate(4);
bytes.putInt((int) object);
type = Type.INTEGER;
}
else if(object instanceof BigDecimal) {
bytes = ByteBuffer.allocate(8);
bytes.putDouble((double) ((BigDecimal) object).doubleValue());
type = Type.DOUBLE;
}
else if(object instanceof Tag) {
bytes = ByteBuffer.wrap(
object.toString().getBytes(StandardCharsets.UTF_8));
type = Type.TAG;
}
else if(object instanceof Timestamp) {
try {
bytes = ByteBuffer.allocate(8);
bytes.putLong(((Timestamp) object).getMicros());
type = Type.TIMESTAMP;
}
catch (IllegalStateException e) {
throw new UnsupportedOperationException(
"Cannot convert string based Timestamp to a TObject");
}
}
else {
bytes = ByteBuffer.wrap(
object.toString().getBytes(StandardCharsets.UTF_8));
type = Type.STRING;
}
bytes.rewind();
return new TObject(bytes, type).setJavaFormat(object);
}
}
/**
* Convert a JSON formatted string to a mapping that associates each key
* with the Java objects that represent the corresponding values. This
* method is designed to parse simple JSON structures that associate keys to
* simple values or arrays without knowing the type of each element ahead of
* time.
*
* This method can properly handle JSON strings that abide by the following
* rules:
*
* The top level element in the JSON string must be an Object
* No nested objects (e.g. a key cannot map to an object)
* No null values
*
*
*
* @param json
* @return the converted data
*/
public static Multimap jsonToJava(String json) {
try (JsonReader reader = new JsonReader(new StringReader(json))) {
reader.setLenient(true);
return jsonToJava(reader);
}
catch (IOException e) {
throw CheckedExceptions.wrapAsRuntimeException(e);
}
}
/**
* Serialize the {@link list} of {@link Multimap maps} with data to a JSON
* string that can be batch inserted into Concourse.
*
* @param list the list of data {@link Multimap maps} to include in the JSON
* object. This is meant to map to the return value of
* {@link #anyJsonToJava(String)}
* @return the JSON string representation of the {@code list}
*/
@SuppressWarnings("unchecked")
public static String mapsToJson(Collection> list) {
// GH-116: The signature declares that the list should contain Multimap
// instances, but we check the type of each element in case the data is
// coming from a JVM dynamic language (i.e. Groovy) that has syntactic
// sugar for a java.util.Map
StringBuilder sb = new StringBuilder();
sb.append('[');
for (Object map : list) {
if(map instanceof Multimap) {
Multimap map0 = (Multimap) map;
sb.append(mapToJson(map0));
sb.append(",");
}
else if(map instanceof Map) {
Map map0 = (Map) map;
sb.append(mapToJson(map0));
sb.append(",");
}
else {
((Multimap) map).getClass(); // force
// ClassCastException
// to be thrown
}
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
/**
* Serialize the {@code map} of data as a JSON object string that can be
* inserted into Concourse.
*
* @param map data to include in the JSON object.
* @return the JSON string representation of the {@code map}
*/
public static String mapToJson(Map map) {
return DataServices.gson().toJson(map);
}
/**
* Serialize the {@code map} of a data as a JSON string that can inserted
* into Concourse.
*
* @param map the data to include in the JSON object. This is meant to map
* to the return value of {@link #jsonToJava(String)}
* @return the JSON string representation of the {@code map}
*/
public static String mapToJson(Multimap map) {
return mapToJson(map.asMap());
}
/**
* Convert the {@code operator} to a string representation.
*
* @param operator
* @return the operator string
*/
public static String operatorToString(Operator operator) {
String string = "";
switch (operator) {
case EQUALS:
string = "=";
break;
case NOT_EQUALS:
string = "!=";
break;
case GREATER_THAN:
string = ">";
break;
case GREATER_THAN_OR_EQUALS:
string = ">=";
break;
case LESS_THAN:
string = "<";
break;
case LESS_THAN_OR_EQUALS:
string = "<=";
break;
case BETWEEN:
string = "><";
break;
default:
string = operator.name();
break;
}
return string;
}
/**
* For a scalar object that may be a {@link TObject} or a collection of
* other objects that may contain {@link TObject TObjects}, convert to the
* appropriate java representation.
*
* @param tobject the possible TObject or collection of TObjects
* @return the java representation
*/
@SuppressWarnings("unchecked")
public static T possibleThriftToJava(Object tobject) {
if(tobject instanceof TObject) {
return (T) thriftToJava((TObject) tobject);
}
else if(tobject instanceof List) {
return (T) Lists.transform((List>) tobject,
Conversions.possibleThriftToJava());
}
else if(tobject instanceof Set) {
return (T) Transformers.transformSetLazily((Set) tobject,
Conversions.possibleThriftToJava());
}
else if(tobject instanceof Map) {
return (T) Transformers.transformMapEntries(
(Map) tobject,
Conversions.possibleThriftToJava(),
Conversions.possibleThriftToJava());
}
else {
return (T) tobject;
}
}
/**
* Analyze {@code value} and convert it to the appropriate Java primitive or
* Object.
*
*
Conversion Rules
*
* String - the value is converted to a string if it
* starts and ends with matching single (') or double ('') quotes.
* Alternatively, the value is converted to a string if it cannot be
* converted to another type
* {@link ResolvableLink} - the value is converted to a
* ResolvableLink if it is a properly formatted specification returned from
* the {@link #stringToResolvableLinkSpecification(String, String)} method
* (NOTE: this is a rare case)
* {@link Link} - the value is converted to a Link if
* it is an int or long that is prepended by an '@' sign (i.e. @1234)
* Boolean - the value is converted to a Boolean if it
* is equal to 'true', or 'false' regardless of case
* Double - the value is converted to a double if and
* only if it is a decimal number that is immediately followed by a single
* capital "D" (e.g. 3.14D)
* Tag - the value is converted to a Tag if it starts
* and ends with matching (`) quotes
* Integer, Long, Float - the value is converted to a
* non double number depending upon whether it is a standard integer (e.g.
* less than {@value java.lang.Integer#MAX_VALUE}), a long, or a floating
* point decimal
*
*
*
*
* @param value
* @return the converted value
*/
public static Object stringToJava(String value) {
if(value.isEmpty()) {
return value;
}
char first = value.charAt(0);
char last = value.charAt(value.length() - 1);
Long record;
if(AnyStrings.isWithinQuotes(value, TAG_MARKER)) {
// keep value as string since its between single or double quotes
return value.substring(1, value.length() - 1);
}
else if(first == '@' && (record = Longs
.tryParse(value.substring(1, value.length()))) != null) {
return Link.to(record);
}
else if(first == '@' && last == '@'
&& STRING_RESOLVABLE_LINK_REGEX.matcher(value).matches()) {
String ccl = value.substring(1, value.length() - 1);
return ResolvableLink.create(ccl);
}
else if(value.equalsIgnoreCase("true")) {
return true;
}
else if(value.equalsIgnoreCase("false")) {
return false;
}
else if(first == TAG_MARKER && last == TAG_MARKER) {
return Tag.create(value.substring(1, value.length() - 1));
}
else if(first == '|' && last == '|') {
value = value.substring(1, value.length() - 1);
String[] toks = value.split("\\|");
Timestamp timestamp;
if(toks.length == 1) {
// #value is a timestring that intends to rely on either one of
// the built-in DateTimeFormatters or the natural language
// translation in order to figure out the microseconds with
// which to create the Timestamp
timestamp = Timestamp
.fromMicros(NaturalLanguage.parseMicros(value));
}
else {
// #value looks like timestring|format in which case the second
// part is the DateTimeFormatter to use for getting the
// microseconds with which to create the Timestamp
// Valid formatting options can be found at
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html
DateTimeFormatter formatter = DateTimeFormat
.forPattern(toks[1]);
timestamp = Timestamp.parse(toks[0], formatter);
}
return timestamp;
}
else {
return MoreObjects.firstNonNull(AnyStrings.tryParseNumber(value),
value);
}
}
/**
* Convert the {@code symbol} into the appropriate {@link Operator}.
*
* @param symbol - the string form of a symbol (i.e. =, >, >=, etc) or a
* CaSH shortcut (i.e. eq, gt, gte, etc)
* @return the {@link Operator} that is parsed from the string
* {@code symbol}
*/
public static Operator stringToOperator(String symbol) {
Operator operator = OPERATOR_STRINGS.get(symbol);
if(operator == null) {
throw new IllegalStateException(
"Cannot parse " + symbol + " into an operator");
}
else {
return operator;
}
}
/**
*
* Users are encouraged to use {@link Link#toWhere(String)} instead of this
* method.
*
*
* USE WITH CAUTION: This conversation is only necessary
* when bulk inserting data in string form (i.e. importing data from a CSV
* file) that should have static links dynamically resolved.
* Unless you are certain otherwise, you should never need to use this
* method because there is probably some intermediate function or framework
* that does this for you!
*
*
* Convert the {@code ccl} string to a {@link ResolvableLink} instruction
* for the receiver to add links to all the records that match the criteria.
*
*
* Please note that this method only returns a specification and not an
* actual {@link ResolvableLink} object. Use the
* {@link #stringToJava(String)} method on the value returned from this
* method to get the object.
*
*
* @param ccl - The criteria to use when resolving link targets
* @return An instruction to create a {@link ResolvableLink}
*/
public static String stringToResolvableLinkInstruction(String ccl) {
return AnyStrings.joinSimple(RAW_RESOLVABLE_LINK_SYMBOL_PREPEND, ccl,
RAW_RESOLVABLE_LINK_SYMBOL_APPEND);
}
/**
*
* USE WITH CAUTION: This conversation is only necessary
* for applications that import raw data but cannot use the Concourse API
* directly and therefore cannot explicitly add links (e.g. the
* import-framework that handles raw string data).
* If you have access to the Concourse API, you should not use this
* method!
*
* Convert the {@code rawValue} into a {@link ResolvableLink} specification
* that instructs the receiver to add a link to all the records that have
* {@code rawValue} mapped from {@code key}.
*
* Please note that this method only returns a specification and not an
* actual {@link ResolvableLink} object. Use the
* {@link #stringToJava(String)} method on the value returned from this
* method to get the object.
*
*
* @param key
* @param rawValue
* @return the transformed value.
*/
@Deprecated
public static String stringToResolvableLinkSpecification(String key,
String rawValue) {
return stringToResolvableLinkInstruction(
AnyStrings.joinWithSpace(key, "=", rawValue));
}
/**
* Return a Set that represents the Java representation of each of the
* {@code TObjects} in the input Set.
*
* @param objects a Set of TObjects
* @return a Set of Java objects
*/
public static Set thriftSetToJava(Set tobjects) {
Set java = Sets
.newLinkedHashSetWithExpectedSize(tobjects.size());
thriftCollectionToJava(tobjects, java);
return java;
}
/**
* Return the Java Object that represents {@code object}.
*
* @param object
* @return the Object
*/
public static Object thriftToJava(TObject object) {
Preconditions.checkState(object.getType() != null,
"Cannot read value because it has been "
+ "created with a newer version of Concourse "
+ "Server. Please upgrade this client.");
Object java = object.getJavaFormat();
if(java == null) {
ByteBuffer buffer = object.bufferForData();
switch (object.getType()) {
case BOOLEAN:
java = ByteBuffers.getBoolean(buffer);
break;
case DOUBLE:
java = buffer.getDouble();
break;
case FLOAT:
java = buffer.getFloat();
break;
case INTEGER:
java = buffer.getInt();
break;
case LINK:
java = Link.to(buffer.getLong());
break;
case LONG:
java = buffer.getLong();
break;
case TAG:
java = ByteBuffers.getString(buffer);
break;
case TIMESTAMP:
java = Timestamp.fromMicros(buffer.getLong());
break;
case NULL:
java = null;
break;
default:
java = ByteBuffers.getString(buffer);
break;
}
buffer.rewind();
}
return java;
}
/**
* In-place implementation for converting a collection of java objects to a
* typed {@code output} collection of TObjects.
*
* @param input the original collection to convert
* @param output the output collection into which the converted objects are
* placed
*/
private static void javaCollectionToThrift(Collection input,
Collection output) {
for (Object elt : input) {
output.add(javaToThrift(elt));
}
}
/**
* Convert the next JSON object in the {@code reader} to a mapping that
* associates each key with the Java objects that represent the
* corresponding values.
*
*
* This method has the same rules and limitations as
* {@link #jsonToJava(String)}. It simply uses a {@link JsonReader} to
* handle reading an array of objects.
*
*
* This method DOES NOT {@link JsonReader#close()} the
* {@code reader}.
*
*
* @param reader the {@link JsonReader} that contains a stream of JSON
* @return the JSON data in the form of a {@link Multimap} from keys to
* values
*/
private static Multimap jsonToJava(JsonReader reader) {
Multimap data = HashMultimap.create();
try {
reader.beginObject();
JsonToken peek0;
while ((peek0 = reader.peek()) != JsonToken.END_OBJECT) {
String key = reader.nextName();
peek0 = reader.peek();
if(peek0 == JsonToken.BEGIN_ARRAY) {
// If we have an array, add the elements individually. If
// there are any duplicates in the array, they will be
// filtered out by virtue of the fact that a HashMultimap
// does not store dupes.
reader.beginArray();
JsonToken peek = reader.peek();
do {
Object value;
if(peek == JsonToken.BOOLEAN) {
value = reader.nextBoolean();
}
else if(peek == JsonToken.NUMBER) {
value = stringToJava(reader.nextString());
}
else if(peek == JsonToken.STRING) {
String orig = reader.nextString();
value = stringToJava(orig);
if(orig.isEmpty()) {
value = orig;
}
// If the token looks like a string, it MUST be
// converted to a Java string unless it is a
// masquerading double or an instance of Thrift
// translatable class that has a special string
// representation (i.e. Tag, Link)
else if(orig.charAt(orig.length() - 1) != 'D'
&& !CLASSES_WITH_ENCODED_STRING_REPR
.contains(value.getClass())) {
value = value.toString();
}
}
else if(peek == JsonToken.NULL) {
reader.skipValue();
continue;
}
else {
throw new JsonParseException(
"Cannot parse nested object or array within an array");
}
data.put(key, value);
}
while ((peek = reader.peek()) != JsonToken.END_ARRAY);
reader.endArray();
}
else {
Object value;
if(peek0 == JsonToken.BOOLEAN) {
value = reader.nextBoolean();
}
else if(peek0 == JsonToken.NUMBER) {
value = stringToJava(reader.nextString());
}
else if(peek0 == JsonToken.STRING) {
String orig = reader.nextString();
value = stringToJava(orig);
if(orig.isEmpty()) {
value = orig;
}
// If the token looks like a string, it MUST be
// converted to a Java string unless it is a
// masquerading double or an instance of Thrift
// translatable class that has a special string
// representation (i.e. Tag, Link)
else if(orig.charAt(orig.length() - 1) != 'D'
&& !CLASSES_WITH_ENCODED_STRING_REPR
.contains(value.getClass())) {
value = value.toString();
}
}
else if(peek0 == JsonToken.NULL) {
reader.skipValue();
continue;
}
else {
throw new JsonParseException(
"Cannot parse nested object to value");
}
data.put(key, value);
}
}
reader.endObject();
return data;
}
catch (IOException | IllegalStateException e) {
throw new JsonParseException(e.getMessage());
}
}
/**
* In-place implementation for converting a collection of TObjects to a
* typed {@code output} collection of java objects.
*
* @param input the original collection to convert
* @param output the output collection into which the converted objects are
* placed
*/
private static void thriftCollectionToJava(Collection input,
Collection output) {
for (TObject elt : input) {
output.add(thriftToJava(elt));
}
}
private Convert() {/* Utility Class */}
/**
* A special class that is used to indicate that the record(s) to which one
* or more {@link Link links} should point must be resolved by finding all
* records that match a criteria.
*
* This class is NOT part of the public API, so it should not be used as a
* value for input to the client. Objects of this class exist merely to
* provide utilities that depend on the client with instructions for
* resolving a link in cases when the end-user of the utility cannot use the
* client directly themselves (i.e. specifying a resolvable link in a raw
* text file for the import framework).
*
*
* To get an object of this class, call {@link Convert#stringToJava(String)}
* on the result of calling
* {@link Convert#stringToResolvableLinkInstruction(String)} on the raw
* data.
*
*
* @author Jeff Nelson
*/
@Immutable
public static final class ResolvableLink {
// NOTE: This class does not define #hashCode() or #equals() because the
// defaults are the desired behaviour
/**
* Create a new {@link ResolvableLink} that provides instructions to
* create links to all the records that match the {@code ccl} string.
*
* @param ccl - The criteria to use when resolving link targets
* @return the ResolvableLink
*/
@PackagePrivate
static ResolvableLink create(String ccl) {
return new ResolvableLink(ccl);
}
/**
* Create a new {@link ResolvableLink} that provides instructions to
* create a link to the records which contain {@code value} for
* {@code key}.
*
* @param key
* @param value
* @return the ResolvableLink
*/
@PackagePrivate
@Deprecated
static ResolvableLink newResolvableLink(String key, Object value) {
return new ResolvableLink(key, value);
}
@Deprecated
protected final String key;
@Deprecated
protected final Object value;
/**
* The CCL string that should be used when resolving the link targets.
*/
private final String ccl;
/**
* Construct a new instance.
*
* @param ccl - The criteria to use when resolving link targets
*/
private ResolvableLink(String ccl) {
this.ccl = ccl;
this.key = null;
this.value = null;
}
/**
* Construct a new instance.
*
* @param key
* @param value
* @deprecated As of version 0.5.0
*/
@Deprecated
private ResolvableLink(String key, Object value) {
this.ccl = new StringBuilder().append(key).append(" = ")
.append(value).toString();
this.key = key;
this.value = value;
}
/**
* Return the {@code ccl} string that should be used for resolving link
* targets.
*
* @return {@link #ccl}
*/
public String getCcl() {
return ccl;
}
/**
* Return the associated key.
*
* @return the key
*/
@Deprecated
public String getKey() {
return key;
}
/**
* Return the associated value.
*
* @return the value
*/
@Deprecated
public Object getValue() {
return value;
}
@Override
public String toString() {
return AnyStrings.format("{} for {}",
this.getClass().getSimpleName(), ccl);
}
}
}