package act.data;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* 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.
* #L%
*/
import act.app.App;
import act.data.util.StringOrPattern;
import act.util.ActContext;
import act.util.PropertySpec;
import org.osgl.$;
import org.osgl.util.C;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
class OutputFieldsCache {
// Key to index final output fields. Key includes:
// 1. excluded - developer declared excluded field list
// 2. outputs - developer declared output field list
// 3. component type - the type of the entity where field data get extracted
private class K {
Set<String> excluded;
List<String> outputs;
Class<?> componentType;
K(Set<String> ss, List<String> ls, Class<?> componentType) {
excluded = ss;
outputs = ls;
this.componentType = componentType;
}
@Override
public int hashCode() {
return $.hc(excluded, outputs, componentType);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof K) {
K that = (K) obj;
return $.eq(that.excluded, this.excluded)
&& $.eq(that.outputs, this.outputs)
&& $.eq(that.componentType, this.componentType);
}
return false;
}
}
private Map<K, List<String>> cache = C.newMap();
public List<String> getOutputFields(PropertySpec.MetaInfo spec, Class<?> componentClass, ActContext context) {
K k = new K(spec.excludedFields(context), spec.outputFields(context), componentClass);
List<String> outputs = cache.get(k);
if (null == outputs) {
outputs = calculateOutputs(k);
cache.put(k, outputs);
}
return outputs;
}
List<String> calculateOutputs(K k) {
Class<?> type = k.componentType;
if ($.isSimpleType(type) && k.excluded.isEmpty() && k.outputs.isEmpty()) {
return C.list();
}
C.List<StringOrPattern> outputs = C.newList();
boolean hasPattern = hasPattern(k.outputs, outputs);
Set<String> excluded = k.excluded;
if (hasPattern || outputs.isEmpty()) {
DataPropertyRepository repo = App.instance().service(DataPropertyRepository.class);
List<String> allFields = repo.propertyListOf(k.componentType);
if (!excluded.isEmpty()) {
List<String> finalOutputs;
List<StringOrPattern> lsp = C.newList();
boolean excludeHasPattern = hasPattern(excluded, lsp);
if (!excludeHasPattern) {
return C.list(allFields).without(excluded);
} else {
finalOutputs = C.newList(allFields);
outer:
for (String s : allFields) {
for (StringOrPattern sp: lsp) {
if (sp.matches(s)) {
finalOutputs.remove(s);
continue outer;
}
}
}
return finalOutputs;
}
} else {
if (outputs.isEmpty()) {
return allFields;
}
// excluded is empty and output fields has pattern
List<String> finalOutputs = C.newList();
for (StringOrPattern sp: outputs) {
if (sp.isPattern()) {
Pattern p = sp.p();
for (String s: allFields) {
if (p.matcher(s).matches()) {
finalOutputs.add(s);
}
}
} else {
finalOutputs.add(sp.s());
}
}
return finalOutputs;
}
} else {
return k.outputs;
}
}
private boolean hasPattern(Collection<String> ls, List<StringOrPattern> lsp) {
boolean b = false;
for (String s: ls) {
StringOrPattern sp = new StringOrPattern(s);
b = b || sp.isPattern();
lsp.add(sp);
}
return b;
}
}