/*
* 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.addthis.hydra.data.query;
import java.util.ArrayList;
import java.util.List;
import com.addthis.basis.util.LessBytes;
import com.addthis.basis.util.LessStrings;
import com.addthis.bundle.core.BundleField;
import com.addthis.bundle.core.BundleFormat;
import com.addthis.bundle.value.ValueFactory;
import com.addthis.bundle.value.ValueObject;
import com.addthis.codec.annotations.FieldConfig;
import com.addthis.codec.binary.CodecBin2;
import com.addthis.codec.codables.Codable;
import com.addthis.hydra.data.tree.DataTreeNode;
import com.addthis.hydra.data.tree.DataTreeNodeActor;
import org.apache.commons.lang3.mutable.MutableInt;
/**
* For retrieval of node data attachments by name/key
*
* @user-reference
*/
public class QueryElementField implements Codable {
private static final BoundedValue[] memKey = new BoundedValue[0];
// output column this element is bound to 'show' (or null if dropped)
@FieldConfig(codable = true)
private String column;
@FieldConfig(codable = true)
public String name;
@FieldConfig(codable = true)
public BoundedValue[] keys;
private BundleField field;
public QueryElementField parse(String tok, MutableInt nextColumn) {
if (tok.startsWith("+")) {
// show = true;
column = Integer.toString(nextColumn.intValue());
nextColumn.increment();
tok = tok.substring(1);
} else if (tok.startsWith("?")) {
// show = true;
column = Integer.toString(nextColumn.intValue());
nextColumn.increment();
tok = tok.substring(1);
keys = memKey;
}
String[] kv = LessStrings.splitArray(LessBytes.urldecode(tok), "=");
if (kv.length == 2) {
name = kv[0];
String[] keyarr = LessStrings.splitArray(kv[1], ",");
keys = new BoundedValue[keyarr.length];
for (int i = 0; i < keyarr.length; i++) {
keys[i] = new BoundedValue().parse(keyarr[i], nextColumn);
}
} else if (kv.length == 1) {
name = kv[0];
if (keys == null) {
keys = new BoundedValue[1];
keys[0] = new BoundedValue();
}
}
return this;
}
void toCompact(StringBuilder sb) {
if (mem()) {
sb.append("?");
} else if (show()) {
sb.append("+");
}
sb.append(LessBytes.urlencode(name != null ? name : ""));
if (keys != null && !mem() && keys.length > 0) {
sb.append("=");
for (BoundedValue bv : keys) {
bv.toCompact(sb);
}
}
}
public boolean show() {
return column != null;// show != null && show.booleanValue();
}
public boolean mem() {
return keys != null && keys.length == 0;
}
public BundleField field(BundleFormat format) {
if (field == null) {
field = format.getField(column);
}
return field;
}
public List<ValueObject> getValues(DataTreeNode node) {
if (keys == null) {
return new ArrayList<>();
}
ArrayList<ValueObject> ret = new ArrayList<>(keys.length);
synchronized (node) {
DataTreeNodeActor actor = node.getData(name);
if (actor != null) {
if (keys.length == 0) {
int size = -1;
try {
size = CodecBin2.encodeBytes(actor).length;
} catch (Exception e) {
} finally {
ret.add(ValueFactory.create(size));
}
return ret;
}
for (int i = 0; i < keys.length; i++) {
ValueObject qv = actor.onValueQuery(keys[i].name);
if (keys[i].bounded) {
try {
ret.add(keys[i].validate(qv.asLong().getLong()) ? qv : null);
} catch (NumberFormatException ex) {
ret.add(null);
}
} else {
if (qv == null) {
ret.add(null);
continue;
}
if (qv.getObjectType() == ValueObject.TYPE.ARRAY) {
for (ValueObject o : qv.asArray()) {
ret.add(o);
}
} else {
ret.add(qv);
}
}
}
return ret;
}
}
for (int i = 0; i < keys.length; i++) {
ret.add(null);
}
return ret;
}
}