/* * 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.facebook.presto.client; import com.facebook.presto.spi.type.NamedTypeSignature; import com.facebook.presto.spi.type.ParameterKind; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.TypeSignature; import com.facebook.presto.spi.type.TypeSignatureParameter; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import javax.annotation.concurrent.Immutable; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.regex.Pattern; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; @Immutable public class ClientTypeSignature { private static final Pattern PATTERN = Pattern.compile(".*[<>,].*"); private final String rawType; private final List<ClientTypeSignatureParameter> arguments; public ClientTypeSignature(TypeSignature typeSignature) { this( typeSignature.getBase(), Lists.transform(typeSignature.getParameters(), ClientTypeSignatureParameter::new)); } public ClientTypeSignature(String rawType, List<ClientTypeSignatureParameter> arguments) { this(rawType, ImmutableList.of(), ImmutableList.of(), arguments); } @JsonCreator public ClientTypeSignature( @JsonProperty("rawType") String rawType, @JsonProperty("typeArguments") List<ClientTypeSignature> typeArguments, @JsonProperty("literalArguments") List<Object> literalArguments, @JsonProperty("arguments") List<ClientTypeSignatureParameter> arguments) { requireNonNull(rawType, "rawType is null"); this.rawType = rawType; checkArgument(!rawType.isEmpty(), "rawType is empty"); checkArgument(!PATTERN.matcher(rawType).matches(), "Bad characters in rawType type: %s", rawType); if (arguments != null) { this.arguments = unmodifiableList(new ArrayList<>(arguments)); } else { requireNonNull(typeArguments, "typeArguments is null"); requireNonNull(literalArguments, "literalArguments is null"); ImmutableList.Builder<ClientTypeSignatureParameter> convertedArguments = ImmutableList.builder(); // Talking to a legacy server (< 0.133) if (rawType.equals(StandardTypes.ROW)) { checkArgument(typeArguments.size() == literalArguments.size()); for (int i = 0; i < typeArguments.size(); i++) { Object value = literalArguments.get(i); checkArgument(value instanceof String, "Expected literalArgument %d in %s to be a string", i, literalArguments); convertedArguments.add(new ClientTypeSignatureParameter(TypeSignatureParameter.of(new NamedTypeSignature((String) value, toTypeSignature(typeArguments.get(i)))))); } } else { checkArgument(literalArguments.isEmpty(), "Unexpected literal arguments from legacy server"); for (ClientTypeSignature typeArgument : typeArguments) { convertedArguments.add(new ClientTypeSignatureParameter(ParameterKind.TYPE, typeArgument)); } } this.arguments = convertedArguments.build(); } } private static TypeSignature toTypeSignature(ClientTypeSignature signature) { List<TypeSignatureParameter> parameters = signature.getArguments().stream() .map(ClientTypeSignature::legacyClientTypeSignatureParameterToTypeSignatureParameter) .collect(toList()); return new TypeSignature(signature.getRawType(), parameters); } private static TypeSignatureParameter legacyClientTypeSignatureParameterToTypeSignatureParameter(ClientTypeSignatureParameter parameter) { switch (parameter.getKind()) { case LONG: throw new UnsupportedOperationException("Unexpected long type literal returned by legacy server"); case TYPE: return TypeSignatureParameter.of(toTypeSignature(parameter.getTypeSignature())); case NAMED_TYPE: return TypeSignatureParameter.of(parameter.getNamedTypeSignature()); default: throw new UnsupportedOperationException("Unknown parameter kind " + parameter.getKind()); } } @JsonProperty public String getRawType() { return rawType; } @JsonProperty public List<ClientTypeSignatureParameter> getArguments() { return arguments; } /** * This field is deprecated and clients should switch to {@link #getArguments()} */ @Deprecated @JsonProperty public List<ClientTypeSignature> getTypeArguments() { List<ClientTypeSignature> result = new ArrayList<>(); for (ClientTypeSignatureParameter argument : arguments) { switch (argument.getKind()) { case TYPE: result.add(argument.getTypeSignature()); break; case NAMED_TYPE: result.add(new ClientTypeSignature(argument.getNamedTypeSignature().getTypeSignature())); break; default: return new ArrayList<>(); } } return result; } /** * This field is deprecated and clients should switch to {@link #getArguments()} */ @Deprecated @JsonProperty public List<Object> getLiteralArguments() { List<Object> result = new ArrayList<>(); for (ClientTypeSignatureParameter argument : arguments) { switch (argument.getKind()) { case NAMED_TYPE: result.add(argument.getNamedTypeSignature().getName()); break; default: return new ArrayList<>(); } } return result; } @Override public String toString() { if (rawType.equals(StandardTypes.ROW)) { return rowToString(); } else { StringBuilder typeName = new StringBuilder(rawType); if (!arguments.isEmpty()) { typeName.append("("); boolean first = true; for (ClientTypeSignatureParameter argument : arguments) { if (!first) { typeName.append(","); } first = false; typeName.append(argument.toString()); } typeName.append(")"); } return typeName.toString(); } } @Deprecated private String rowToString() { String fields = arguments.stream() .map(ClientTypeSignatureParameter::getNamedTypeSignature) .map(parameter -> format("%s %s", parameter.getName(), parameter.getTypeSignature().toString())) .collect(Collectors.joining(",")); return format("row(%s)", fields); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ClientTypeSignature other = (ClientTypeSignature) o; return Objects.equals(this.rawType.toLowerCase(Locale.ENGLISH), other.rawType.toLowerCase(Locale.ENGLISH)) && Objects.equals(this.arguments, other.arguments); } @Override public int hashCode() { return Objects.hash(rawType.toLowerCase(Locale.ENGLISH), arguments); } }