All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jolie.lang.parse.module.SymbolReferenceResolver Maven / Gradle / Ivy

/*
 * Copyright (C) 2020 Narongrit Unwerawattana 
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */

package jolie.lang.parse.module;

import java.net.URI;
import java.util.*;

import jolie.lang.CodeCheckMessage;
import jolie.lang.Constants;
import jolie.lang.Constants.OperandType;
import jolie.lang.parse.UnitOLVisitor;
import jolie.lang.parse.ast.AddAssignStatement;
import jolie.lang.parse.ast.AssignStatement;
import jolie.lang.parse.ast.CompareConditionNode;
import jolie.lang.parse.ast.CompensateStatement;
import jolie.lang.parse.ast.CorrelationSetInfo;
import jolie.lang.parse.ast.CorrelationSetInfo.CorrelationAliasInfo;
import jolie.lang.parse.ast.CorrelationSetInfo.CorrelationVariableInfo;
import jolie.lang.parse.ast.CurrentHandlerStatement;
import jolie.lang.parse.ast.DeepCopyStatement;
import jolie.lang.parse.ast.DefinitionCallStatement;
import jolie.lang.parse.ast.DefinitionNode;
import jolie.lang.parse.ast.DivideAssignStatement;
import jolie.lang.parse.ast.DocumentationComment;
import jolie.lang.parse.ast.EmbedServiceNode;
import jolie.lang.parse.ast.EmbeddedServiceNode;
import jolie.lang.parse.ast.ExecutionInfo;
import jolie.lang.parse.ast.ExitStatement;
import jolie.lang.parse.ast.ForEachArrayItemStatement;
import jolie.lang.parse.ast.ForEachSubNodeStatement;
import jolie.lang.parse.ast.ForStatement;
import jolie.lang.parse.ast.IfStatement;
import jolie.lang.parse.ast.ImportStatement;
import jolie.lang.parse.ast.ImportableSymbol.AccessModifier;
import jolie.lang.parse.ast.InputPortInfo;
import jolie.lang.parse.ast.InputPortInfo.AggregationItemInfo;
import jolie.lang.parse.ast.InstallFixedVariableExpressionNode;
import jolie.lang.parse.ast.InstallStatement;
import jolie.lang.parse.ast.InterfaceDefinition;
import jolie.lang.parse.ast.InterfaceExtenderDefinition;
import jolie.lang.parse.ast.LinkInStatement;
import jolie.lang.parse.ast.LinkOutStatement;
import jolie.lang.parse.ast.MultiplyAssignStatement;
import jolie.lang.parse.ast.NDChoiceStatement;
import jolie.lang.parse.ast.NotificationOperationStatement;
import jolie.lang.parse.ast.NullProcessStatement;
import jolie.lang.parse.ast.OLSyntaxNode;
import jolie.lang.parse.ast.OneWayOperationDeclaration;
import jolie.lang.parse.ast.OneWayOperationStatement;
import jolie.lang.parse.ast.OperationDeclaration;
import jolie.lang.parse.ast.OutputPortInfo;
import jolie.lang.parse.ast.ParallelStatement;
import jolie.lang.parse.ast.PointerStatement;
import jolie.lang.parse.ast.PostDecrementStatement;
import jolie.lang.parse.ast.PostIncrementStatement;
import jolie.lang.parse.ast.PreDecrementStatement;
import jolie.lang.parse.ast.PreIncrementStatement;
import jolie.lang.parse.ast.Program;
import jolie.lang.parse.ast.ProvideUntilStatement;
import jolie.lang.parse.ast.RequestResponseOperationDeclaration;
import jolie.lang.parse.ast.RequestResponseOperationStatement;
import jolie.lang.parse.ast.RunStatement;
import jolie.lang.parse.ast.SequenceStatement;
import jolie.lang.parse.ast.ServiceNode;
import jolie.lang.parse.ast.ServiceNodeJava;
import jolie.lang.parse.ast.SolicitResponseOperationStatement;
import jolie.lang.parse.ast.SpawnStatement;
import jolie.lang.parse.ast.SubtractAssignStatement;
import jolie.lang.parse.ast.SynchronizedStatement;
import jolie.lang.parse.ast.ThrowStatement;
import jolie.lang.parse.ast.TypeCastExpressionNode;
import jolie.lang.parse.ast.UndefStatement;
import jolie.lang.parse.ast.ValueVectorSizeExpressionNode;
import jolie.lang.parse.ast.VariablePathNode;
import jolie.lang.parse.ast.WhileStatement;
import jolie.lang.parse.ast.courier.CourierChoiceStatement;
import jolie.lang.parse.ast.courier.CourierChoiceStatement.InterfaceOneWayBranch;
import jolie.lang.parse.ast.courier.CourierChoiceStatement.InterfaceRequestResponseBranch;
import jolie.lang.parse.ast.courier.CourierChoiceStatement.OperationOneWayBranch;
import jolie.lang.parse.ast.courier.CourierChoiceStatement.OperationRequestResponseBranch;
import jolie.lang.parse.ast.courier.CourierDefinitionNode;
import jolie.lang.parse.ast.courier.NotificationForwardStatement;
import jolie.lang.parse.ast.courier.SolicitResponseForwardStatement;
import jolie.lang.parse.ast.expression.*;
import jolie.lang.parse.ast.expression.InlineTreeExpressionNode.Operation;
import jolie.lang.parse.ast.types.TypeChoiceDefinition;
import jolie.lang.parse.ast.types.TypeDefinition;
import jolie.lang.parse.ast.types.TypeDefinitionLink;
import jolie.lang.parse.ast.types.TypeDefinitionUndefined;
import jolie.lang.parse.ast.types.TypeInlineDefinition;
import jolie.lang.parse.context.ParsingContext;
import jolie.lang.parse.module.ModuleCrawler.CrawlerResult;
import jolie.lang.parse.module.SymbolInfo.Scope;
import jolie.lang.parse.module.exceptions.DuplicateSymbolException;
import jolie.lang.parse.module.exceptions.IllegalAccessSymbolException;
import jolie.lang.parse.module.exceptions.SymbolNotFoundException;
import jolie.util.Pair;

public class SymbolReferenceResolver {
	private static CodeCheckMessage buildSymbolNotFoundError( OLSyntaxNode node, String name ) {
		return CodeCheckMessage.buildWithoutHelp( node, "Symbol not found: " + name
			+ " is not defined in this module (the symbol could not be retrieved from the symbol table)" );
	}

	private static CodeCheckMessage buildInfiniteTypeDefinitionLinkLoop( OLSyntaxNode node, String name ) {
		return CodeCheckMessage.buildWithoutHelp( node, "Type definition link loop detected: " + name
			+ " (this might mean that the referred type has not been defined or could not be retrieved)" );
	}

	// private static CodeCheckingError buildSymbolNotFoundError( OLSyntaxNode node,
	// String name,
	// ImportPath path ) {
	// return CodeCheckingError.build( node, "Symbol not found: " + name + " is not
	// defined in module "
	// + path
	// + " (the symbol could not be retrieved from the symbol table)" );
	// }

	private static CodeCheckMessage buildSymbolTypeMismatchError( OLSyntaxNode node, String symbolName,
		String expectedType, String actualType ) {
		return CodeCheckMessage.buildWithoutHelp( node, "Symbol is used incorrectly: " + symbolName + " is used as "
			+ expectedType + ", but it actually has type " + actualType );
	}

	private static CodeCheckMessage buildMissingServiceInputportError( ServiceNode node, String portId ) {
		return CodeCheckMessage.buildWithoutHelp( node,
			"Unable to bind operations to port " + portId + ": " + node.name()
				+ " service doesn't have an inputPort with location 'local' defined." );
	}

	// private static CodeCheckingError buildTypeLinkOutOfBoundError(
	// TypeDefinitionLink node ) {
	// return CodeCheckingError.build( node, "Unable to find linked type: " +
	// node.name()
	// + " has infinite loop to it's linked type" );
	// }

	private class SymbolReferenceResolverVisitor implements UnitOLVisitor {
		private URI currentURI;
		private final List< CodeCheckMessage > errors = new ArrayList<>();

		protected SymbolReferenceResolverVisitor() {}

		private void error( CodeCheckMessage e ) {
			errors.add( e );
		}

		private boolean isValid() {
			return errors.isEmpty();
		}

		/**
		 * Walk through the Jolie AST tree and resolve all symbol references
		 */
		public void resolve( Program p ) throws ModuleException {
			currentURI = p.context().source();
			visit( p );
			if( !isValid() ) {
				throw new ModuleException( errors );
			}
		}

		@Override
		public void visit( Program n ) {
			for( OLSyntaxNode node : n.children() ) {
				if( !isValid() ) {
					return;
				}
				node.accept( this );
			}
		}

		@Override
		public void visit( OneWayOperationDeclaration decl ) {
			decl.requestType().accept( this );
		}

		@Override
		public void visit( RequestResponseOperationDeclaration decl ) {
			decl.requestType().accept( this );
			decl.responseType().accept( this );
			for( TypeDefinition fault : decl.faults().values() ) {
				fault.accept( this );
			}
		}

		@Override
		public void visit( DefinitionNode n ) {
			n.body().accept( this );
		}

		@Override
		public void visit( ParallelStatement n ) {
			for( OLSyntaxNode node : n.children() ) {
				node.accept( this );
			}
		}

		@Override
		public void visit( SequenceStatement n ) {
			for( OLSyntaxNode node : n.children() ) {
				node.accept( this );
			}
		}

		@Override
		public void visit( NDChoiceStatement n ) {
			for( Pair< OLSyntaxNode, OLSyntaxNode > child : n.children() ) {
				child.key().accept( this );
				child.value().accept( this );
			}
		}

		@Override
		public void visit( OneWayOperationStatement n ) {}

		@Override
		public void visit( RequestResponseOperationStatement n ) {
			n.process().accept( this );
		}

		@Override
		public void visit( NotificationOperationStatement n ) {}

		@Override
		public void visit( SolicitResponseOperationStatement n ) {
			if( n.handlersFunction() != null ) {
				for( Pair< String, OLSyntaxNode > handler : n.handlersFunction().pairs() ) {
					handler.value().accept( this );
				}
			}
		}

		@Override
		public void visit( LinkInStatement n ) {}

		@Override
		public void visit( LinkOutStatement n ) {}

		@Override
		public void visit( AssignStatement n ) {
			n.expression().accept( this );
		}

		@Override
		public void visit( AddAssignStatement n ) {
			n.expression().accept( this );
		}

		@Override
		public void visit( SubtractAssignStatement n ) {
			n.expression().accept( this );
		}

		@Override
		public void visit( MultiplyAssignStatement n ) {
			n.expression().accept( this );
		}

		@Override
		public void visit( DivideAssignStatement n ) {
			n.expression().accept( this );
		}

		@Override
		public void visit( IfStatement n ) {
			for( Pair< OLSyntaxNode, OLSyntaxNode > child : n.children() ) {
				child.key().accept( this );
				child.value().accept( this );
			}
			if( n.elseProcess() != null ) {
				n.elseProcess().accept( this );
			}
		}

		@Override
		public void visit( DefinitionCallStatement n ) {}

		@Override
		public void visit( WhileStatement n ) {
			n.condition().accept( this );
			n.body().accept( this );
		}

		@Override
		public void visit( OrConditionNode n ) {
			for( OLSyntaxNode node : n.children() ) {
				node.accept( this );
			}
		}

		@Override
		public void visit( AndConditionNode n ) {
			for( OLSyntaxNode node : n.children() ) {
				node.accept( this );
			}
		}

		@Override
		public void visit( NotExpressionNode n ) {
			n.expression().accept( this );
		}

		@Override
		public void visit( CompareConditionNode n ) {
			n.leftExpression().accept( this );
			n.rightExpression().accept( this );
		}

		@Override
		public void visit( ConstantIntegerExpression n ) {}

		@Override
		public void visit( ConstantDoubleExpression n ) {}

		@Override
		public void visit( ConstantBoolExpression n ) {}

		@Override
		public void visit( ConstantLongExpression n ) {}

		@Override
		public void visit( ConstantStringExpression n ) {}

		@Override
		public void visit( ProductExpressionNode n ) {
			for( Pair< OperandType, OLSyntaxNode > node : n.operands() ) {
				node.value().accept( this );
			}
		}

		@Override
		public void visit( SumExpressionNode n ) {
			for( Pair< OperandType, OLSyntaxNode > node : n.operands() ) {
				node.value().accept( this );
			}
		}

		@Override
		public void visit( VariableExpressionNode n ) {}

		@Override
		public void visit( NullProcessStatement n ) {}

		@Override
		public void visit( jolie.lang.parse.ast.Scope n ) {
			n.body().accept( this );
		}

		@Override
		public void visit( InstallStatement n ) {
			for( Pair< String, OLSyntaxNode > handlerFunction : n.handlersFunction().pairs() ) {
				handlerFunction.value().accept( this );
			}
		}

		@Override
		public void visit( CompensateStatement n ) {}

		@Override
		public void visit( ThrowStatement n ) {
			if( n.expression() != null ) {
				n.expression().accept( this );
			}
		}

		@Override
		public void visit( ExitStatement n ) {}

		@Override
		public void visit( ExecutionInfo n ) {}

		@Override
		public void visit( CorrelationSetInfo n ) {
			for( CorrelationVariableInfo cSetVar : n.variables() ) {
				for( CorrelationAliasInfo aliases : cSetVar.aliases() ) {
					aliases.guardName().accept( this );
				}
			}
		}

		@Override
		public void visit( InputPortInfo n ) {
			// resolve interface definition
			for( InterfaceDefinition iface : n.getInterfaceList() ) {
				Optional< SymbolInfo > symbol = getSymbol( iface.context(), iface.name() );
				if( !symbol.isPresent() ) {
					error( buildSymbolNotFoundError( iface, iface.name() ) );
					return;
				}
				if( !(symbol.get().node() instanceof InterfaceDefinition) ) {
					error( buildSymbolTypeMismatchError( iface, iface.name(), "InterfaceDefinition",
						symbol.get().node().getClass().getSimpleName() ) );
					return;
				}
				InterfaceDefinition ifaceDeclFromSymbol = (InterfaceDefinition) symbol.get().node();
				ifaceDeclFromSymbol.operationsMap().values().forEach( op -> {
					iface.addOperation( op );
					n.addOperation( op );
				} );
				ifaceDeclFromSymbol.getDocumentation().ifPresent( iface::setDocumentation );
			}
			for( OperationDeclaration op : n.operations() ) {
				op.accept( this );
			}
			for( AggregationItemInfo aggregationItem : n.aggregationList() ) {
				if( aggregationItem.interfaceExtender() != null ) {
					aggregationItem.interfaceExtender().accept( this );
				}
			}
		}

		@Override
		public void visit( OutputPortInfo n ) {
			// resolve interface definition
			for( InterfaceDefinition iface : n.getInterfaceList() ) {
				Optional< SymbolInfo > symbol = getSymbol( iface.context(), iface.name() );
				if( !symbol.isPresent() ) {
					error( buildSymbolNotFoundError( iface, iface.name() ) );
					return;
				}
				if( !(symbol.get().node() instanceof InterfaceDefinition) ) {
					error( buildSymbolTypeMismatchError( iface, iface.name(), "InterfaceDefinition",
						symbol.get().node().getClass().getSimpleName() ) );
					return;
				}
				InterfaceDefinition ifaceDeclFromSymbol = (InterfaceDefinition) symbol.get().node();
				ifaceDeclFromSymbol.operationsMap().values().forEach( op -> {
					iface.addOperation( op );
					n.addOperation( op );
				} );
				ifaceDeclFromSymbol.getDocumentation().ifPresent( iface::setDocumentation );
			}
			for( OperationDeclaration op : n.operations() ) {
				op.accept( this );
			}
		}

		@Override
		public void visit( PointerStatement n ) {}

		@Override
		public void visit( DeepCopyStatement n ) {
			n.rightExpression().accept( this );
		}

		@Override
		public void visit( RunStatement n ) {}

		@Override
		public void visit( UndefStatement n ) {}

		@Override
		public void visit( ValueVectorSizeExpressionNode n ) {}

		@Override
		public void visit( PreIncrementStatement n ) {}

		@Override
		public void visit( PostIncrementStatement n ) {}

		@Override
		public void visit( PreDecrementStatement n ) {}

		@Override
		public void visit( PostDecrementStatement n ) {}

		@Override
		public void visit( ForStatement n ) {
			n.body().accept( this );
		}

		@Override
		public void visit( ForEachSubNodeStatement n ) {
			n.body().accept( this );
		}

		@Override
		public void visit( ForEachArrayItemStatement n ) {
			n.body().accept( this );
		}

		@Override
		public void visit( SpawnStatement n ) {
			n.body().accept( this );
		}

		@Override
		public void visit( IsTypeExpressionNode n ) {}

		@Override
		public void visit( InstanceOfExpressionNode n ) {
			n.type().accept( this );
		}

		@Override
		public void visit( TypeCastExpressionNode n ) {}

		@Override
		public void visit( SynchronizedStatement n ) {
			n.body().accept( this );
		}

		@Override
		public void visit( CurrentHandlerStatement n ) {}

		@Override
		public void visit( EmbeddedServiceNode n ) {
			if( n.program() != null ) {
				n.program().accept( this );
			}
		}

		@Override
		public void visit( InstallFixedVariableExpressionNode n ) {}

		@Override
		public void visit( VariablePathNode n ) {}

		@Override
		public void visit( TypeInlineDefinition n ) {
			if( n.hasSubTypes() ) {
				for( Map.Entry< String, TypeDefinition > subType : n.subTypes() ) {
					subType.getValue().accept( this );
				}
			}
		}

		@Override
		public void visit( IfExpressionNode n ) {
			n.guard().accept( this );
			n.thenExpression().accept( this );
			n.elseExpression().accept( this );
		}

		@Override
		public void visit( TypeDefinitionLink n ) {
			TypeDefinition linkedType;
			if( n.linkedTypeName().equals( TypeDefinitionUndefined.UNDEFINED_KEYWORD ) ) {
				linkedType = TypeDefinitionUndefined.getInstance();
			} else {
				Optional< SymbolInfo > targetSymbolInfo = getSymbol( n.context(), n.linkedTypeName() );
				if( !targetSymbolInfo.isPresent() ) {
					error( buildSymbolNotFoundError( n, n.linkedTypeName() ) );
					return;
				}
				if( !(targetSymbolInfo.get().node() instanceof TypeDefinition) ) {
					error( buildSymbolTypeMismatchError( n, n.name(), "TypeDefinition",
						targetSymbolInfo.get().node().getClass().getSimpleName() ) );
					return;
				}
				linkedType = (TypeDefinition) targetSymbolInfo.get().node();
				if( linkedType == null ) {
					error( buildSymbolNotFoundError( n, n.linkedTypeName() ) );
					return;
				} else if( linkedType.equals( n ) ) {
					error( buildInfiniteTypeDefinitionLinkLoop( n, n.linkedTypeName() ) );
					return;
				}
			}
			n.setLinkedType( linkedType );
		}

		@Override
		public void visit( InterfaceDefinition n ) {
			for( OperationDeclaration op : n.operationsMap().values() ) {
				op.accept( this );
			}
		}

		@Override
		public void visit( DocumentationComment n ) {}

		@Override
		public void visit( FreshValueExpressionNode n ) {}

		@Override
		public void visit( CourierDefinitionNode n ) {
			n.body().accept( this );
		}

		@Override
		public void visit( CourierChoiceStatement n ) {
			for( InterfaceOneWayBranch owIfaceBranch : n.interfaceOneWayBranches() ) {
				InterfaceDefinition iface = owIfaceBranch.interfaceDefinition;

				Optional< SymbolInfo > symbol = getSymbol( iface.context(), iface.name() );
				if( !symbol.isPresent() ) {
					error( buildSymbolNotFoundError( iface, iface.name() ) );
					return;
				}
				if( !(symbol.get().node() instanceof InterfaceDefinition) ) {
					error( buildSymbolTypeMismatchError( iface, iface.name(), "InterfaceDefinition",
						symbol.get().node().getClass().getSimpleName() ) );
					return;
				}
				InterfaceDefinition ifaceDeclFromSymbol = (InterfaceDefinition) symbol.get().node();
				ifaceDeclFromSymbol.operationsMap().values().forEach( op -> {
					iface.addOperation( op );
					op.accept( this );
				} );
				ifaceDeclFromSymbol.getDocumentation().ifPresent( iface::setDocumentation );

				owIfaceBranch.body.accept( this );
			}

			for( InterfaceRequestResponseBranch rrIfaceBranch : n
				.interfaceRequestResponseBranches() ) {
				InterfaceDefinition iface = rrIfaceBranch.interfaceDefinition;

				Optional< SymbolInfo > symbol = getSymbol( iface.context(), iface.name() );
				if( !symbol.isPresent() ) {
					error( buildSymbolNotFoundError( iface, iface.name() ) );
					return;
				}
				if( !(symbol.get().node() instanceof InterfaceDefinition) ) {
					error( buildSymbolTypeMismatchError( iface, iface.name(), "InterfaceDefinition",
						symbol.get().node().getClass().getSimpleName() ) );
					return;
				}
				InterfaceDefinition ifaceDeclFromSymbol = (InterfaceDefinition) symbol.get().node();
				ifaceDeclFromSymbol.operationsMap().values().forEach( op -> {
					iface.addOperation( op );
					op.accept( this );
				} );
				ifaceDeclFromSymbol.getDocumentation().ifPresent( iface::setDocumentation );

				rrIfaceBranch.body.accept( this );
			}

			for( OperationOneWayBranch owBranch : n.operationOneWayBranches() ) {
				owBranch.body.accept( this );
			}

			for( OperationRequestResponseBranch rrBranch : n.operationRequestResponseBranches() ) {
				rrBranch.body.accept( this );
			}
		}

		@Override
		public void visit( NotificationForwardStatement n ) {}

		@Override
		public void visit( SolicitResponseForwardStatement n ) {}

		@Override
		public void visit( InterfaceExtenderDefinition n ) {
			if( n.defaultOneWayOperation() != null ) {
				n.defaultOneWayOperation().accept( this );
			}
			if( n.defaultRequestResponseOperation() != null ) {
				n.defaultRequestResponseOperation().accept( this );
			}
			for( OperationDeclaration op : n.operationsMap().values() ) {
				op.accept( this );
			}
		}

		@Override
		public void visit( InlineTreeExpressionNode n ) {
			for( Operation operation : n.operations() ) {
				if( operation instanceof InlineTreeExpressionNode.AssignmentOperation ) {
					((InlineTreeExpressionNode.AssignmentOperation) operation).expression()
						.accept( this );
				} else if( operation instanceof InlineTreeExpressionNode.DeepCopyOperation ) {
					((InlineTreeExpressionNode.DeepCopyOperation) operation).expression()
						.accept( this );
				}
			}
		}

		@Override
		public void visit( VoidExpressionNode n ) {}

		@Override
		public void visit( ProvideUntilStatement n ) {
			n.provide().accept( this );
			n.until().accept( this );
		}

		@Override
		public void visit( TypeChoiceDefinition n ) {
			n.left().accept( this );
			if( n.right() != null ) {
				n.right().accept( this );
			}
		}

		@Override
		public void visit( ImportStatement n ) {}

		private Optional< SymbolInfo > getSymbol( ParsingContext context, String name ) {
			if( symbolTables.containsKey( context.source() )
				&& symbolTables.get( context.source() ).getSymbol( name ).isPresent() ) {
				return symbolTables.get( context.source() ).getSymbol( name );
			} else if( symbolTables.containsKey( currentURI )
				&& symbolTables.get( currentURI ).getSymbol( name ).isPresent() ) {
				return symbolTables.get( currentURI ).getSymbol( name );
			}
			return Optional.empty();
		}

		@Override
		public void visit( ServiceNode n ) {
			n.parameterConfiguration().ifPresent( config -> config.type().accept( this ) );
			n.program().accept( this );
		}

		@Override
		public void visit( EmbedServiceNode n ) {
			Optional< SymbolInfo > targetSymbolInfo = getSymbol( n.context(), n.serviceName() );
			if( !targetSymbolInfo.isPresent() ) {
				error( buildSymbolNotFoundError( n, n.serviceName() ) );
				return;
			}
			if( !(targetSymbolInfo.get().node() instanceof ServiceNode) ) {
				error( buildSymbolTypeMismatchError( n, n.serviceName(), "ServiceNode",
					targetSymbolInfo.get().node().getClass().getSimpleName() ) );
				return;
			}
			ServiceNode embeddingService = (ServiceNode) targetSymbolInfo.get().node();
			embeddingService.accept( this );
			n.setService( embeddingService );
			if( n.isNewPort() ) {
				if( n.service().type() == Constants.EmbeddedServiceType.SERVICENODE ) {
					bindServiceOperationsToOutputPort( n.service(), n.bindingPort() );
				} else if( n.service().type() == Constants.EmbeddedServiceType.SERVICENODE_JAVA ) {
					ServiceNodeJava service = (ServiceNodeJava) n.service();
					if( service.inputPortInfo() != null ) {
						bindServiceOperationsToOutputPort( service, n.bindingPort() );
					} else {
						error( buildMissingServiceInputportError( service, n.bindingPort().id() ) );
					}
				}
			}
		}

		@Override
		public void visit( SolicitResponseExpressionNode n ) {}
	}

	/**
	 * Scans through inputPorts with 'local' location in the service node n, and assign the interfaces
	 * and operation to an outputPort op
	 * 
	 * @param n service to perform operations binding
	 * @param op target outputport
	 */
	private void bindServiceOperationsToOutputPort( ServiceNode n, OutputPortInfo op ) {
		// binds operation from ServiceNode to port
		InterfacesAndOperations publicIfacesAndOps = getInterfacesFromInputPortLocal( n );
		for( InterfaceDefinition iface : publicIfacesAndOps.interfaces() ) {
			op.addInterface( iface );
			iface.operationsMap().values().forEach( op::addOperation );
		}
		for( OperationDeclaration oper : publicIfacesAndOps.operations() ) {
			op.addOperation( oper );
		}
	}

	private final Map< URI, ModuleRecord > moduleMap;
	private final Map< URI, SymbolTable > symbolTables;

	private SymbolReferenceResolver( CrawlerResult moduleMap ) {
		this.moduleMap = moduleMap.toMap();
		this.symbolTables = new HashMap<>();
		for( ModuleRecord mr : this.moduleMap.values() ) {
			this.symbolTables.put( mr.uri(), mr.symbolTable() );
		}
	}

	/**
	 * perform a lookup in the module record collection for the original symbol of an importing symbol
	 * 
	 * @param symbolInfo an importing symbol
	 * @param lookedSources a set of sources that are already considered
	 */
	private SymbolInfo symbolSourceLookup( ImportedSymbolInfo symbolInfo, Set< URI > lookedSources )
		throws SymbolNotFoundException {
		ModuleRecord externalSourceRecord = this.moduleMap.get( symbolInfo.moduleSource().get().uri() );
		if( externalSourceRecord == null ) {
			throw new SymbolNotFoundException( symbolInfo.originalSymbolName(),
				symbolInfo.moduleSource().get().uri().toString() );
		}
		Optional< SymbolInfo > externalSourceSymbol = externalSourceRecord.symbolTable()
			.getSymbol( symbolInfo.originalSymbolName() );
		if( !externalSourceSymbol.isPresent() || lookedSources.contains( externalSourceRecord.uri() ) ) {
			throw new SymbolNotFoundException( symbolInfo.originalSymbolName(), symbolInfo.importPath() );
		}
		lookedSources.add( externalSourceRecord.uri() );
		if( externalSourceSymbol.get().scope() == Scope.LOCAL ) {
			return externalSourceSymbol.get();
		} else {
			return symbolSourceLookup( (ImportedSymbolInfo) externalSourceSymbol.get(), lookedSources );
		}
	}

	private SymbolInfo symbolSourceLookup( ImportedSymbolInfo symbolInfo )
		throws SymbolNotFoundException {
		return symbolSourceLookup( symbolInfo, new HashSet<>() );
	}

	/**
	 * resolve externalSymbol by find and set its corresponding AST node by perform lookup at
	 * ModuleRecord Map, a result from ModuleCrawler.
	 */
	private void resolveExternalSymbols()
		throws SymbolNotFoundException, IllegalAccessSymbolException, DuplicateSymbolException {
		for( ModuleRecord md : moduleMap.values() ) {
			for( ImportedSymbolInfo importedSymbol : md.symbolTable().importedSymbolInfos() ) {
				if( importedSymbol instanceof WildcardImportedSymbolInfo ) {
					ModuleRecord wildcardImportedRecord =
						this.moduleMap.get( importedSymbol.moduleSource().get().uri() );
					md.symbolTable().resolveWildcardImport( (WildcardImportedSymbolInfo) importedSymbol,
						wildcardImportedRecord.symbolTable().symbols() );
				} else if( importedSymbol.node() == null ) {
					SymbolInfo targetSymbol = symbolSourceLookup( importedSymbol );
					if( targetSymbol.accessModifier() == AccessModifier.PRIVATE ) {
						throw new IllegalAccessSymbolException( importedSymbol.name(),
							importedSymbol.importPath() );
					}
					importedSymbol.resolve( targetSymbol.node() );
				}
			}
		}
	}

	/**
	 * resolve LinkedType of each ModuleRecord AST node in the Map.
	 * 
	 * @throws ModuleException if the linked type cannot find its referencing node
	 */
	private void resolveLinkedTypes() throws ModuleException {
		SymbolReferenceResolverVisitor resolver = new SymbolReferenceResolverVisitor();
		for( ModuleRecord md : moduleMap.values() ) {
			resolver.resolve( md.program() );
		}
	}

	/**
	 * Resolve symbols the is imported from external modules and resolve linked type's pointer
	 * 
	 * @throws ModuleException if the process is failed
	 */
	public static void resolve( CrawlerResult moduleMap ) throws ModuleException {
		SymbolReferenceResolver resolver = new SymbolReferenceResolver( moduleMap );
		try {
			resolver.resolveExternalSymbols();
		} catch( SymbolNotFoundException | IllegalAccessSymbolException
			| DuplicateSymbolException e ) {
			CodeCheckMessage message = CodeCheckMessage.withoutHelp( null, e.getMessage() );
			throw new ModuleException( message );
		}
		resolver.resolveLinkedTypes();
	}

	/**
	 * An utility class represents interfaces and operations
	 */
	private static class InterfacesAndOperations {
		private final List< InterfaceDefinition > ifaces;
		private final List< OperationDeclaration > ops;

		private InterfacesAndOperations() {
			ifaces = new ArrayList<>();
			ops = new ArrayList<>();
		}

		public InterfaceDefinition[] interfaces() {
			return ifaces.toArray( new InterfaceDefinition[] {} );
		}

		public OperationDeclaration[] operations() {
			return ops.toArray( new OperationDeclaration[] {} );
		}
	}

	/**
	 * retrieves InterfaceDefinitions from ServiceNode's inputPorts declared with 'local' location
	 * 
	 * @param node a service node object
	 * @return InterfaceDefinition[] list of interfaces of local incoming communication port
	 * 
	 */
	private static InterfacesAndOperations getInterfacesFromInputPortLocal(
		ServiceNode node ) {
		Map< String, OutputPortInfo > internalOp = new HashMap<>();
		InterfacesAndOperations result = new InterfacesAndOperations();

		for( OLSyntaxNode n : node.program().children() ) {
			if( n instanceof OutputPortInfo ) {
				OutputPortInfo op = (OutputPortInfo) n;
				internalOp.put( op.id(), op );
			} else if( n instanceof InputPortInfo ) {
				InputPortInfo ip = (InputPortInfo) n;
				if( ip.location() instanceof ConstantStringExpression ) {
					String location = ((ConstantStringExpression) ip.location()).value();
					if( location.equals( Constants.LOCAL_LOCATION_KEYWORD ) ) {
						result.ifaces.addAll( ip.getInterfaceList() );
						for( InputPortInfo.AggregationItemInfo item : ip.aggregationList() ) {
							for( String opName : item.outputPortList() ) {
								OutputPortInfo op = internalOp.get( opName );
								result.ifaces.addAll( op.getInterfaceList() );
							}
						}
						result.ops.addAll( ip.operations() );
					}
				}
			}
		}
		return result;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy