org.prorefactor.treeparser.FrameStack Maven / Gradle / Ivy
/********************************************************************************
* Copyright (c) 2003-2015 John Green
* Copyright (c) 2015-2024 Riverside Software
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU Lesser General Public License v3.0
* which is available at https://www.gnu.org/licenses/lgpl-3.0.txt
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-3.0
********************************************************************************/
package org.prorefactor.treeparser;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import org.antlr.v4.runtime.tree.ParseTree;
import org.prorefactor.core.ABLNodeType;
import org.prorefactor.core.JPNode;
import org.prorefactor.core.nodetypes.FieldRefNode;
import org.prorefactor.core.nodetypes.RecordNameNode;
import org.prorefactor.core.schema.Field;
import org.prorefactor.core.schema.IField;
import org.prorefactor.proparse.antlr4.Proparse;
import org.prorefactor.treeparser.symbols.FieldBuffer;
import org.prorefactor.treeparser.symbols.FieldContainer;
import org.prorefactor.treeparser.symbols.Symbol;
import org.prorefactor.treeparser.symbols.TableBuffer;
import org.prorefactor.treeparser.symbols.Variable;
import org.prorefactor.treeparser.symbols.widgets.Browse;
import org.prorefactor.treeparser.symbols.widgets.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Keeps a stack of most recently "referenced" frames. A frame may be "referenced" at up to two different occassions.
* Once when the frame is created (like in a DEFINE FRAME statement), and once when the frame is "initialized" (like in
* a DISPLAY statement). The frame's scope is determined at the time it is initialized. Also deals with BROWSE widgets
* and the fields in those.
*/
public class FrameStack {
private static final Logger LOG = LoggerFactory.getLogger(FrameStack.class);
private boolean currStatementIsEnabler = false;
private Deque frameMRU = new LinkedList<>();
private FieldContainer containerForCurrentStatement = null;
private JPNode currStatementWholeTableFormItemNode = null;
protected FrameStack() {
// Only from TreeParser
}
/**
* The ID node in a BROWSE ID pair. The ID node might have already had the symbol assigned to it at the point where
* the statement head was processed.
*/
void browseRefNode(JPNode idNode, TreeParserSymbolScope symbolScope) {
LOG.trace("Enter FrameStack#browseRefNode");
if (idNode.getSymbol() == null)
browseRefSet(idNode, symbolScope);
}
private Browse browseRefSet(JPNode idNode, TreeParserSymbolScope symbolScope) {
Browse browse = (Browse) symbolScope.lookupFieldLevelWidget(idNode.getText());
idNode.setSymbol(browse);
return browse;
}
/**
* For a Form_item node which is for a whole table reference, get a list of the FieldBuffers that would be added to
* the frame, respecting any EXCEPT fields list.
*/
private List calculateFormItemTableFields(JPNode formItemNode) {
assert formItemNode.getType() == Proparse.Form_item;
assert formItemNode.getFirstChild().getNodeType() == ABLNodeType.RECORD_NAME;
RecordNameNode recordNameNode = (RecordNameNode) formItemNode.getFirstChild();
TableBuffer tableBuffer = recordNameNode.getTableBuffer();
HashSet fieldSet = new HashSet<>(tableBuffer.getTable().getFieldSet());
JPNode exceptNode = formItemNode.getParent().findDirectChild(Proparse.EXCEPT);
if (exceptNode != null)
for (JPNode n = exceptNode.getFirstChild(); n != null; n = n.getNextSibling()) {
if (!(n instanceof FieldRefNode))
continue;
IField f = ((FieldBuffer) ((FieldRefNode) n).getSymbol()).getField();
fieldSet.remove(f);
}
ArrayList returnList = new ArrayList<>();
for (IField field : fieldSet) {
returnList.add(tableBuffer.getFieldBuffer(field));
}
return returnList;
}
/**
* Create a frame object. Adds the new frame object to the MRU list.
*/
private Frame createFrame(String frameName, TreeParserSymbolScope symbolScope) {
Frame frame = new Frame(frameName, symbolScope);
frameMRU.addFirst(frame);
return frame;
}
/**
* Recieve a Form_item node for a field which should be referenceable on the frame|browse. This checks for LEXAT
* (DISPLAY thisField @ anotherField) which would keep thisField from being added to the frame. The LEXAT is dealt
* with in a separate call. This must be called after any Field_ref symbols have been resolved. This only does
* anything if the first child of the Form_item is RECORD_NAME or Field_ref. Tree parser rules like display_item and
* form_item sometimes get used in statements that don't actually affect frames. In those cases,
* containerForCurrentStatement==null, and this function is a no-op.
*/
void formItem(JPNode formItemNode) {
LOG.trace("Enter FrameStack#formItem");
if (containerForCurrentStatement == null)
return;
assert formItemNode.getType() == Proparse.Form_item;
JPNode firstChild = formItemNode.getFirstChild();
if (firstChild.getNodeType() == ABLNodeType.RECORD_NAME) {
// Delay processing until the end of the statement. We need any EXCEPT fields resolved first.
currStatementWholeTableFormItemNode = formItemNode;
} else {
FieldRefNode fieldRefNode = null;
JPNode tempNode = formItemNode.findDirectChild(Proparse.Format_phrase);
if (tempNode != null) {
tempNode = tempNode.findDirectChild(Proparse.LEXAT);
if (tempNode != null)
return;
}
if (fieldRefNode == null && firstChild.getType() == Proparse.Field_ref) {
fieldRefNode = (FieldRefNode) firstChild;
}
if (fieldRefNode != null)
containerForCurrentStatement.addSymbol(fieldRefNode.getSymbol(), currStatementIsEnabler);
}
}
/**
* The ID node in a FRAME ID pair. For "WITH FRAME id", the ID was already set when we processed the statement head.
*/
void frameRefNode(JPNode idNode, TreeParserSymbolScope symbolScope) {
LOG.trace("Enter FrameStack#frameRefNode");
if (idNode.getSymbol() == null)
frameRefSet(idNode, symbolScope);
}
private Frame frameRefSet(JPNode idNode, TreeParserSymbolScope symbolScope) {
String frameName = idNode.getText();
Frame frame = (Frame) symbolScope.lookupWidget(Proparse.FRAME, frameName);
if (frame == null)
frame = createFrame(frameName, symbolScope);
idNode.setSymbol(frame);
return frame;
}
/** For a statement that might have #(WITH ... #([FRAME|BROWSE] ID)), get the FRAME|BROWSE node. */
private JPNode getContainerTypeNode(JPNode stateNode) {
JPNode withNode = stateNode.findDirectChild(Proparse.WITH);
if (withNode == null)
return null;
JPNode typeNode = withNode.findDirectChild(Proparse.FRAME);
if (typeNode == null)
typeNode = withNode.findDirectChild(Proparse.BROWSE);
return typeNode;
}
/** Create the frame if necessary, set its scope if that hasn't already been done. */
private Frame initializeFrame(Frame frame, Block currentBlock) {
// If we don't have a frame then get or create the unnamed default frame for the block.
if (frame == null)
frame = currentBlock.getDefaultFrame();
boolean newFrame = frame == null;
if (newFrame) {
frame = createFrame("", currentBlock.getSymbolScope());
frame.setFrameScopeUnnamedDefault(currentBlock);
}
if (!frame.isInitialized()) {
frame.initialize(currentBlock);
if (!newFrame) {
frameMRU.remove(frame);
frameMRU.addFirst(frame);
}
}
return frame;
}
/**
* Deals with frame fields referenced by INPUT and USING. For a Field_ref node where it matches #(Field_ref INPUT
* ...), determine which frame field is being referenced. This is also called for #(RECORD_NAME ... #(USING
* #(Field_ref...))). Sets the FieldContainer attribute (a Frame or Browse object) on the Field_ref node.
*
* @see org.prorefactor.core.JPNode#getFieldContainer().
*/
FieldLookupResult inputFieldLookup(FieldRefNode fieldRefNode, TreeParserSymbolScope currentScope) {
JPNode idNode = fieldRefNode.getIdNode();
Field.Name inputName = new Field.Name(idNode.getText().toLowerCase());
FieldContainer fieldContainer = null;
Symbol fieldOrVariable = null;
JPNode tempNode = fieldRefNode.getFirstChild();
int tempType = tempNode.getType();
if (tempType == Proparse.INPUT) {
tempNode = tempNode.getNextSibling();
tempType = tempNode.getType();
}
if (tempType == Proparse.BROWSE || tempType == Proparse.FRAME) {
fieldContainer = (FieldContainer) tempNode.getNextNode().getSymbol();
fieldOrVariable = fieldContainer.lookupFieldOrVar(inputName);
} else {
for (Frame frame : frameMRU) {
if (!frame.getScope().isActiveIn(currentScope))
continue;
fieldOrVariable = frame.lookupFieldOrVar(inputName);
if (fieldOrVariable != null) {
fieldContainer = frame;
break;
}
}
}
if (fieldOrVariable == null) {
LOG.error("Could not find input field {} {}:{}", idNode.getText(), idNode.getFileName(), idNode.getLine());
return null;
}
fieldRefNode.setFieldContainer(fieldContainer);
FieldLookupResult.Builder result = new FieldLookupResult.Builder().setSymbol(fieldOrVariable);
if (!(fieldOrVariable instanceof Variable)) {
Field.Name resName = new Field.Name(fieldOrVariable.fullName());
if (inputName.getTable() == null)
result.setUnqualified();
if (inputName.getField().length() < resName.getField().length()
|| (inputName.getTable() != null && (inputName.getTable().length() < resName.getTable().length())))
result.setAbbreviated();
}
return result.build();
}
/** Receive the node (will be a Field_ref) that follows an @ in a frame phrase. */
void lexAt(JPNode fieldRefNode) {
LOG.trace("Enter FrameStack#lexAt");
if (containerForCurrentStatement != null)
containerForCurrentStatement.addSymbol(fieldRefNode.getSymbol(), currStatementIsEnabler);
}
/** FOR|REPEAT|DO blocks need to be checked for explicit WITH FRAME phrase. */
void nodeOfBlock(JPNode blockNode, Block currentBlock) {
LOG.trace("Enter FrameStack#nodeOfBlock");
JPNode containerTypeNode = getContainerTypeNode(blockNode);
if (containerTypeNode == null)
return;
// No such thing as DO WITH BROWSE...
assert containerTypeNode.getType() == Proparse.FRAME;
JPNode frameIDNode = containerTypeNode.getNextNode();
assert frameIDNode.getType() == Proparse.ID;
Frame frame = frameRefSet(frameIDNode, currentBlock.getSymbolScope());
frame.setFrameScopeBlockExplicitDefault(blockNode.getBlock());
blockNode.setFieldContainer(frame);
containerForCurrentStatement = frame;
}
/** Called at tree parser DEFINE BROWSE statement. */
void nodeOfDefineBrowse(Browse newBrowseSymbol, JPNode defNode, ParseTree defNode2) {
LOG.trace("Enter FrameStack#nodeOfDefineBrowse");
containerForCurrentStatement = newBrowseSymbol;
containerForCurrentStatement.addStatement(defNode2);
}
/**
* Called at tree parser DEFINE FRAME statement. A DEFINE FRAME statement might hide a frame symbol from a higher
* symbol scope. A DEFINE FRAME statement is legal for a frame symbol already in use, sort of like how you can have
* multiple FORM statements, I suppose. A DEFINE FRAME statement does not initialize the frame's scope.
*/
void nodeOfDefineFrame(ParseTree defNode2, JPNode defNode, JPNode idNode, String frameName, TreeParserSymbolScope currentSymbolScope) {
LOG.trace("Enter FrameStack#nodeOfDefineFrame");
Frame frame = (Frame) currentSymbolScope.lookupSymbolLocally(Proparse.FRAME, frameName);
if (frame == null)
frame = createFrame(frameName, currentSymbolScope);
frame.setDefinitionNode(defNode.getIdNode());
defNode.setSymbol(frame);
defNode.setFieldContainer(frame);
containerForCurrentStatement = frame;
containerForCurrentStatement.addStatement(defNode2);
}
/**
* For an IO/UI statement which would initialize a frame, compute the frame and set the frame attribute on the
* statement head node. This is not used from DEFINE FRAME, HIDE FRAME, or any other "frame" statements which would
* not count as a "reference" for frame scoping purposes.
*/
void nodeOfInitializingStatement(ParseTree stateNode2, JPNode stateNode, Block currentBlock) {
LOG.trace("Enter FrameStack#nodeOfInitializingStatement");
JPNode containerTypeNode = getContainerTypeNode(stateNode);
JPNode idNode = null;
if (containerTypeNode != null) {
idNode = containerTypeNode.getNextNode();
assert idNode.getType() == Proparse.ID;
}
if (containerTypeNode != null && containerTypeNode.getType() == Proparse.BROWSE) {
containerForCurrentStatement = browseRefSet(idNode, currentBlock.getSymbolScope());
} else {
Frame frame = null;
if (idNode != null)
frame = frameRefSet(idNode, currentBlock.getSymbolScope());
// This returns the frame whether it already exists or it creates it new.
frame = initializeFrame(frame, currentBlock);
containerForCurrentStatement = frame;
}
stateNode.setFieldContainer(containerForCurrentStatement);
containerForCurrentStatement.addStatement(stateNode2);
}
/**
* For frame init statements like VIEW and CLEAR which have no frame phrase. Called at the end of the statement, after
* all symbols (including FRAME ID) have been resolved.
*/
void simpleFrameInitStatement(ParseTree headNode2, JPNode headNode, JPNode frameIDNode, Block currentBlock) {
LOG.trace("Enter FrameStack#simpleFrameInitStatement");
Frame frame = (Frame) frameIDNode.getNextNode().getSymbol();
assert frame != null;
initializeFrame(frame, currentBlock);
headNode.setFieldContainer(frame);
frame.addStatement(headNode2);
}
/** Called at the end of a frame affecting statement. */
void statementEnd() {
LOG.trace("Enter FrameStack#statementEnd");
// For something like DISPLAY customer, we delay adding the fields to the frame until the end of the statement.
// That's because any fields in an EXCEPT fields phrase need to have their symbols resolved first.
if (currStatementWholeTableFormItemNode != null) {
List fields = calculateFormItemTableFields(currStatementWholeTableFormItemNode);
for (FieldBuffer fieldBuffer : fields) {
containerForCurrentStatement.addSymbol(fieldBuffer, currStatementIsEnabler);
}
currStatementWholeTableFormItemNode = null;
}
containerForCurrentStatement = null;
currStatementIsEnabler = false;
}
/** Used only by the tree parser, for ENABLE|UPDATE|PROMPT-FOR. */
void statementIsEnabler() {
currStatementIsEnabler = true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy