/*******************************************************************************
* PSHDL is a library and (trans-)compiler for PSHDL input. It generates
* output suitable for implementation or simulation of it.
*
* Copyright (C) 2013 Karsten Becker (feedback (at) pshdl (dot) org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This License does not grant permission to use the trade names, trademarks,
* service marks, or product names of the Licensor, except as required for
* reasonable and customary use in describing the origin of the Work.
*
* Contributors:
* Karsten Becker - initial API and implementation
******************************************************************************/
package org.pshdl.model.utils;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import org.pshdl.model.HDLClass;
import org.pshdl.model.HDLEnumRef;
import org.pshdl.model.HDLInterfaceRef;
import org.pshdl.model.HDLVariable;
import org.pshdl.model.HDLVariableRef;
import org.pshdl.model.IHDLObject;
import org.pshdl.model.extensions.FullNameExtension;
import org.pshdl.model.utils.internal.NonSameList;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
public class HDLQuery {
public static abstract class HDLFieldAccess<T extends IHDLObject, K> {
public static enum Quantifier {
ZERO_OR_ONE, ONE, ZERO_OR_MORE, ONE_OR_MORE
}
public final String fieldName;
public final Class<?> type;
public final Quantifier quantifier;
protected HDLFieldAccess(String fieldName, Class<?> type, Quantifier quantifier) {
this.fieldName = fieldName;
this.type = type;
this.quantifier = quantifier;
}
public abstract K getValue(T obj);
public abstract T setValue(T obj, K value);
}
private static class EqualsMatcher<T> implements Predicate<T> {
private final Object equalsTo;
private final boolean invert;
public EqualsMatcher(Object equalsTo, boolean invert) {
this.equalsTo = equalsTo;
this.invert = invert;
}
@Override
public boolean apply(T obj) {
if (equalsTo == null) {
final boolean b = obj == null ? true : false;
if (invert)
return !b;
return b;
}
final boolean equals = equalsTo.equals(obj);
if (invert)
return !equals;
return equals;
}
}
private static class StartsWithMatcher<T> implements Predicate<T> {
private final T equalsTo;
public StartsWithMatcher(T equalsTo) {
this.equalsTo = equalsTo;
}
@Override
public boolean apply(T obj) {
if (obj == null)
return false;
return obj.toString().startsWith(equalsTo.toString());
}
}
private static class FullNameMatcher<K extends IHDLObject> implements Predicate<K> {
private final HDLQualifiedName asRef;
public FullNameMatcher(HDLQualifiedName asRef) {
this.asRef = asRef;
}
@Override
public boolean apply(K input) {
return asRef.equals(FullNameExtension.fullNameOf(input));
}
}
private static class LastSegmentMatcher<T> implements Predicate<T> {
private final HDLQualifiedName equalsTo;
private boolean matchLocally = false;
public LastSegmentMatcher(String equalsTo) {
this.equalsTo = new HDLQualifiedName(equalsTo);
}
public LastSegmentMatcher(HDLQualifiedName fullName, boolean matchLocally) {
this.equalsTo = fullName.getLocalPart();
this.matchLocally = matchLocally;
}
@Override
public boolean apply(T obj) {
if (obj == null)
return false;
if (matchLocally)
return new HDLQualifiedName(obj.toString()).getLocalPart().equals(equalsTo);
return new HDLQualifiedName(obj.toString()).getLastSegment().equals(equalsTo.getLastSegment());
}
}
@SuppressWarnings("rawtypes")
public static class Result<T extends IHDLObject, K> {
private final HDLFieldAccess<T, K> field;
private final IHDLObject from;
private final Class<T> clazz;
private final Predicate matcher;
public Result(IHDLObject from, Class<T> clazz, HDLFieldAccess<T, K> field, Predicate matcher) {
this.from = from;
this.clazz = clazz;
this.field = field;
this.matcher = matcher;
}
public Collection<T> getAll() {
return getAllMatchingObjects();
}
@SuppressWarnings("unchecked")
private Set<T> getAllMatchingObjects() {
final T[] allObjectsOf = from.getAllObjectsOf(clazz, true);
final Set<T> list = new NonSameList<T>();
if (field != null) {
for (final T t : allObjectsOf) {
final K value = field.getValue(t);
if (matcher.apply(value)) {
list.add(t);
}
}
} else {
for (final T t : allObjectsOf) {
if (matcher.apply(t)) {
list.add(t);
}
}
}
return list;
}
public T getFirst() {
final Collection<T> res = getAllMatchingObjects();
if (res.isEmpty())
return null;
final Iterator<T> iterator = res.iterator();
if (iterator.hasNext())
return iterator.next();
return null;
}
@SuppressWarnings("unchecked")
public Result<T, K> or(Predicate<K> value) {
return new Result<T, K>(from, clazz, field, Predicates.or(matcher, value));
}
}
public static class FieldSelector<T extends IHDLObject, K> {
private final HDLFieldAccess<T, K> field;
private final IHDLObject from;
private final Class<T> clazz;
public FieldSelector(Class<T> clazz, IHDLObject from, HDLFieldAccess<T, K> field) {
this.clazz = clazz;
this.from = from;
this.field = field;
}
public Result<T, K> isEqualTo(K value) {
return new Result<T, K>(from, clazz, field, new EqualsMatcher<K>(value, false));
}
public Result<T, K> startsWith(K ifRef) {
return new Result<T, K>(from, clazz, field, new StartsWithMatcher<K>(ifRef));
}
public Result<T, K> lastSegmentIs(String lastSegment) {
return new Result<T, K>(from, clazz, field, new LastSegmentMatcher<K>(lastSegment));
}
public Result<T, K> isNotEqualTo(K value) {
return new Result<T, K>(from, clazz, field, new EqualsMatcher<K>(value, true));
}
public Result<T, K> matchesLocally(HDLQualifiedName fullName) {
return new Result<T, K>(from, clazz, field, new LastSegmentMatcher<K>(fullName, true));
}
public Result<T, K> matches(Predicate<K> predicate) {
return new Result<T, K>(from, clazz, field, predicate);
}
public <I extends IHDLObject> Result<T, I> fullNameIs(HDLQualifiedName asRef) {
if (field != null)
throw new IllegalArgumentException("Can only use fullNameIs on whereObj");
return new Result<T, I>(from, clazz, null, new FullNameMatcher<I>(asRef));
}
public Result<T, K> isType(final HDLClass clazz) {
final Predicate<K> matcher = new Predicate<K>() {
@Override
public boolean apply(K input) {
return ((IHDLObject) input).getClassSet().contains(clazz);
}
};
return matches(matcher);
}
}
public static class Selector<T extends IHDLObject> {
private final IHDLObject from;
private final Class<T> clazz;
public Selector(Class<T> clazz, IHDLObject obj) {
this.clazz = clazz;
this.from = obj;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public <K> FieldSelector<T, K> where(HDLFieldAccess<? super T, K> field) {
return new FieldSelector(clazz, from, field);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public <K> FieldSelector<T, K> whereObj() {
return new FieldSelector(clazz, from, null);
}
}
public static class Source<T extends IHDLObject> {
private final Class<T> clazz;
public Source(Class<T> clazz) {
this.clazz = clazz;
}
public Selector<T> from(IHDLObject obj) {
return new Selector<T>(clazz, obj);
}
}
public static <T extends IHDLObject> Source<T> select(Class<T> clazz) {
return new Source<T>(clazz);
}
public static <K> Predicate<K> isEqualTo(K value) {
return new EqualsMatcher<K>(value, false);
}
public static <K> Predicate<K> startsWith(K value) {
return new StartsWithMatcher<K>(value);
}
public static <K> Predicate<K> isNotEqualTo(K value) {
return new EqualsMatcher<K>(value, true);
}
public static <K> Predicate<K> lastSegmentIs(String value) {
return new LastSegmentMatcher<K>(value);
}
public static <K> Predicate<K> matchesLocally(HDLQualifiedName value) {
return new LastSegmentMatcher<K>(value, true);
}
public static Collection<HDLEnumRef> getEnumRefs(IHDLObject from, HDLVariable hdlVariable) {
return HDLQuery.select(HDLEnumRef.class).from(from).whereObj().fullNameIs(hdlVariable.asRef()).getAll();
}
public static Collection<HDLVariableRef> getVarRefs(IHDLObject from, HDLVariable hdlVariable) {
return HDLQuery.select(HDLVariableRef.class).from(from).whereObj().fullNameIs(hdlVariable.asRef()).getAll();
}
public static Collection<HDLInterfaceRef> getInterfaceRefs(IHDLObject obj, HDLVariable hdlVariable) {
final HDLQualifiedName asRef = hdlVariable.asRef();
final Collection<HDLInterfaceRef> refs = HDLQuery.select(HDLInterfaceRef.class).from(obj).where(HDLInterfaceRef.fHIf).lastSegmentIs(asRef.getLastSegment()).getAll();
for (final Iterator<HDLInterfaceRef> iterator = refs.iterator(); iterator.hasNext();) {
final HDLInterfaceRef hir = iterator.next();
final Optional<HDLVariable> resolveHIf = hir.resolveHIf();
if (resolveHIf.isPresent()) {
final HDLQualifiedName fullNameOf = FullNameExtension.fullNameOf(resolveHIf.get());
if (!asRef.equals(fullNameOf)) {
iterator.remove();
}
} else {
if (!asRef.equals(hir.getHIfRefName())) {
iterator.remove();
}
}
}
return refs;
}
}