package com.tesora.dve.sql.node;
/*
* #%L
* Tesora Inc.
* Database Virtualization Engine
* %%
* Copyright (C) 2011 - 2014 Tesora Inc.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import com.tesora.dve.sql.parser.SourceLocation;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.transform.CopyContext;
public abstract class LanguageNode {
protected Edge<?,? extends LanguageNode> parent = null;
private EngineBlock block = null;
protected SourceLocation location;
public LanguageNode(SourceLocation loc) {
location = loc;
}
protected LanguageNode(LanguageNode ln) {
location = ln.location;
}
public SourceLocation getSourceLocation() {
return location;
}
public void setSourceLocation(SourceLocation sln) {
location = sln;
}
public <T extends LanguageNode> void setParent(Edge<?,T> p) {
this.parent = p;
}
@SuppressWarnings("unchecked")
public <T extends LanguageNode> Edge<?, T> getParentEdge() {
return (Edge<?, T>) this.parent;
}
public LanguageNode getParent() {
return (this.parent == null ? null : this.parent.getParent());
}
public EngineBlock getBlock() {
if (block == null) block = new EngineBlock(this);
return block;
}
public <T> T getDerivedAttribute(DerivedAttribute<T> da, SchemaContext sc) {
return getBlock().getValue(da, sc);
}
// find the enclosing object of type searchFor, stopping either at null or an object of type stopAt
@SuppressWarnings("unchecked")
public <T> T getEnclosing(Class<T> searchFor, Class<?> stopAt) {
LanguageNode p = this;
while(p != null) {
if (searchFor.isInstance(p))
return (T)p;
else if (stopAt != null && stopAt.isInstance(p))
return null;
if (p.getParent() != null)
p = p.getParent();
else
p = null;
}
return null;
}
public abstract <T extends Edge<?,?>> List<T> getEdges();
public <T extends Edge<?,?>> List<T> getNaturalOrderEdges() {
return getEdges();
}
protected <T extends LanguageNode> void replaceChild(Edge<?,T> edge, T newChild) {
edge.set(newChild);
}
public <T extends Edge<? extends LanguageNode,?>> T getAttribute(IEdgeName name) {
List<T> edges = getEdges();
for(T e : edges) {
if (e.getName().matches(name))
return e;
}
return null;
}
@SuppressWarnings("unchecked")
public <T extends LanguageNode> T ifAncestor(Collection<T> any) {
if (any.contains(this))
return (T)this;
else if (parent == null)
return null;
else if (parent.getParent() != null)
return parent.getParent().ifAncestor(any);
else
return null;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private final boolean edgesEqual(LanguageNode other) {
// we're equal if we have the same number of edges, we have all the
// same edges, and the contents are equal.
List<? extends Edge> mine = getEdges();
List<? extends Edge> yours = other.getEdges();
// because the edge order is fixed in each subclass - we can just iterate over each list
// and as soon as we get a not matching name - we're done.
if (mine.size() != yours.size())
return false;
Iterator<? extends Edge> imine = mine.iterator();
Iterator<? extends Edge> iyours = yours.iterator();
while(imine.hasNext() && iyours.hasNext()) {
Edge me = imine.next();
Edge ye = iyours.next();
if (!me.getName().matches(ye.getName()))
return false;
if (!me.schemaEqual(ye))
return false;
}
return true;
}
protected abstract boolean schemaSelfEqual(LanguageNode other);
public final boolean isSchemaEqual(LanguageNode in) {
if (in == this)
return true;
else if (this.getClass().equals(in.getClass())) {
if (!schemaSelfEqual(in)) return false;
return edgesEqual(in);
} else
return false;
}
private final int edgesHashCode() {
int result = 0;
for(Edge<?,?> e : getEdges()) {
result = addSchemaHash(result,e.schemaHash());
}
return result;
}
public final int getSchemaHashCode() {
return addSchemaHash(addSchemaHash(initSchemaHash(),selfHashCode()),edgesHashCode());
}
protected abstract int selfHashCode();
protected int initSchemaHash() {
return this.getClass().hashCode();
}
public static int addSchemaHash(int result, boolean v) {
return addSchemaHash(result, (v ? 1231 : 1237));
}
public static int addSchemaHash(int result, int hc) {
final int prime = 31;
return prime * result + hc;
}
public Edge<?,?> getMatchingAncestor(EdgeName en) {
if (parent == null)
return null;
else if (parent.getName().matches(en))
return parent;
else
return parent.getParent().getMatchingAncestor(en);
}
public abstract LanguageNode copy(CopyContext in);
}