blob: 00c4075d9e63a05be40d93bf8d3a2bbc9b05414e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* 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 org.apache.asterix.om.pointables.cast;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.asterix.common.exceptions.AsterixException;
import org.apache.asterix.om.pointables.AFlatValuePointable;
import org.apache.asterix.om.pointables.AListVisitablePointable;
import org.apache.asterix.om.pointables.ARecordVisitablePointable;
import org.apache.asterix.om.pointables.base.DefaultOpenFieldType;
import org.apache.asterix.om.pointables.base.IVisitablePointable;
import org.apache.asterix.om.pointables.visitor.IVisitablePointableVisitor;
import org.apache.asterix.om.types.ARecordType;
import org.apache.asterix.om.types.ATypeTag;
import org.apache.asterix.om.types.AbstractCollectionType;
import org.apache.asterix.om.types.EnumDeserializer;
import org.apache.asterix.om.types.IAType;
import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
import org.apache.hyracks.algebricks.common.utils.Triple;
import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
/**
* This class is a IVisitablePointableVisitor implementation which recursively
* visit a given record, list or flat value of a given type, and cast it to a
* specified type. For example:
* A record { "hobby": {{"music", "coding"}}, "id": "001", "name":
* "Person Three"} which confirms to closed type ( id: string, name: string,
* hobby: {{string}}? ) can be casted to a open type (id: string )
* Since the open/closed part of a record has a completely different underlying
* memory/storage layout, the visitor will change the layout as specified at
* runtime.
*/
public class ACastVisitor implements IVisitablePointableVisitor<Void, Triple<IVisitablePointable, IAType, Boolean>> {
private final Map<IVisitablePointable, ARecordCaster> raccessorToCaster = new HashMap<>();
private final Map<IVisitablePointable, AListCaster> laccessorToCaster = new HashMap<>();
private final ArrayBackedValueStorage castBuffer = new ArrayBackedValueStorage();
@Override
public Void visit(AListVisitablePointable accessor, Triple<IVisitablePointable, IAType, Boolean> arg)
throws AsterixException {
AListCaster caster = laccessorToCaster.get(accessor);
if (caster == null) {
caster = new AListCaster();
laccessorToCaster.put(accessor, caster);
}
try {
if (arg.second.getTypeTag().equals(ATypeTag.ANY)) {
arg.second = DefaultOpenFieldType.NESTED_OPEN_AUNORDERED_LIST_TYPE;
}
caster.castList(accessor, arg.first, (AbstractCollectionType) arg.second, this);
} catch (Exception e) {
throw new AsterixException(e);
}
return null;
}
@Override
public Void visit(ARecordVisitablePointable accessor, Triple<IVisitablePointable, IAType, Boolean> arg)
throws AsterixException {
ARecordCaster caster = raccessorToCaster.get(accessor);
if (caster == null) {
caster = new ARecordCaster();
raccessorToCaster.put(accessor, caster);
}
try {
if (arg.second.getTypeTag().equals(ATypeTag.ANY)) {
arg.second = DefaultOpenFieldType.NESTED_OPEN_RECORD_TYPE;
}
ARecordType resultType = (ARecordType) arg.second;
caster.castRecord(accessor, arg.first, resultType, this);
} catch (Exception e) {
throw new AsterixException(e);
}
return null;
}
@Override
public Void visit(AFlatValuePointable accessor, Triple<IVisitablePointable, IAType, Boolean> arg)
throws AsterixException {
if (arg.second == null) {
// for open type case
arg.first.set(accessor);
return null;
}
// set the pointer for result
ATypeTag reqTypeTag = (arg.second).getTypeTag();
if (reqTypeTag == ATypeTag.ANY) {
// for open type case
arg.first.set(accessor);
return null;
}
ATypeTag inputTypeTag =
EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(accessor.getByteArray()[accessor.getStartOffset()]);
if (!needPromote(inputTypeTag, reqTypeTag)) {
arg.first.set(accessor);
} else {
try {
castBuffer.reset();
ATypeHierarchy.convertNumericTypeByteArray(accessor.getByteArray(), accessor.getStartOffset(),
accessor.getLength(), reqTypeTag, castBuffer.getDataOutput());
arg.first.set(castBuffer);
} catch (IOException e1) {
throw new AsterixException(
"Type mismatch: cannot cast the " + inputTypeTag + " type to the " + reqTypeTag + " type.");
}
}
return null;
}
private boolean needPromote(ATypeTag tag0, ATypeTag tag1) {
return !(tag0 == tag1 || tag0 == ATypeTag.NULL || tag0 == ATypeTag.MISSING);
}
}