001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2006.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.query.parser.serql;
007:
008: import java.util.ArrayList;
009: import java.util.HashSet;
010: import java.util.List;
011: import java.util.Set;
012:
013: import org.openrdf.query.parser.serql.ast.ASTProjectionElem;
014: import org.openrdf.query.parser.serql.ast.ASTSelect;
015: import org.openrdf.query.parser.serql.ast.ASTString;
016: import org.openrdf.query.parser.serql.ast.ASTVar;
017: import org.openrdf.query.parser.serql.ast.Node;
018: import org.openrdf.query.parser.serql.ast.SyntaxTreeBuilderTreeConstants;
019: import org.openrdf.query.parser.serql.ast.VisitorException;
020:
021: /**
022: * Processes projection aliases, verifying that the specified aliases are unique
023: * and generating aliases for the elements for which no alias has been
024: * specified but that do require one.
025: *
026: * @author Arjohn Kampman
027: */
028: class ProjectionAliasProcessor extends ASTVisitorBase {
029:
030: @Override
031: public Object visit(ASTSelect node, Object data)
032: throws VisitorException {
033: // Iterate over all projection elements to retrieve the defined aliases
034: Set<String> aliases = new HashSet<String>();
035: List<Node> unaliasedNodes = new ArrayList<Node>();
036:
037: for (int i = 0; i < node.jjtGetNumChildren(); i++) {
038: Node projElem = node.jjtGetChild(i);
039:
040: assert projElem instanceof ASTProjectionElem : "child node is not a projection element";
041:
042: Object result = projElem.jjtAccept(this , aliases);
043:
044: if (result instanceof String) {
045: String alias = (String) result;
046:
047: boolean isUnique = aliases.add(alias);
048:
049: if (!isUnique) {
050: throw new VisitorException(
051: "Duplicate projection element aliases: '"
052: + alias + "'");
053: }
054: } else {
055: unaliasedNodes.add(projElem);
056: }
057: }
058:
059: // Iterate over the unaliased nodes and generate aliases for them
060: int aliasNo = 1;
061: for (Node projElem : unaliasedNodes) {
062: Node exprNode = projElem.jjtGetChild(0);
063:
064: if (exprNode instanceof ASTVar) {
065: String varName = ((ASTVar) exprNode).getName();
066:
067: if (!aliases.contains(varName)) {
068: // No need to generate an alias for this element
069: aliases.add(varName);
070: continue;
071: }
072: }
073:
074: // Generate unique alias for projection element
075: String alias;
076: while (aliases.contains(alias = "_" + aliasNo++)) {
077: // try again
078: }
079:
080: aliases.add(alias);
081:
082: ASTString aliasNode = new ASTString(
083: SyntaxTreeBuilderTreeConstants.JJTSTRING);
084: aliasNode.setValue(alias);
085: aliasNode.jjtSetParent(projElem);
086: projElem.jjtAppendChild(aliasNode);
087: }
088:
089: return data;
090: }
091:
092: @Override
093: public Object visit(ASTProjectionElem node, Object data)
094: throws VisitorException {
095: // Only visit alias node
096: if (node.jjtGetNumChildren() >= 2) {
097: return node.jjtGetChild(1).jjtAccept(this , data);
098: }
099:
100: return data;
101: }
102:
103: @Override
104: public String visit(ASTString node, Object data)
105: throws VisitorException {
106: return node.getValue();
107: }
108: }
|