/*******************************************************************************
* Copyright (c) 2011, 2015 Ericsson, Ecole Polytechnique de Montreal and others
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthew Khouzam - Initial Design and Grammar
* Francis Giraldeau - Initial API and implementation
* Simon Marchi - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.internal.ctf.core.event.metadata;
import java.util.ArrayList;
import java.util.List;
import org.antlr.runtime.tree.CommonTree;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.common.core.NonNullUtils;
import org.eclipse.tracecompass.ctf.core.event.CTFClock;
import org.eclipse.tracecompass.ctf.core.event.metadata.DeclarationScope;
import org.eclipse.tracecompass.ctf.core.trace.CTFTrace;
import org.eclipse.tracecompass.ctf.parser.CTFParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.ClockParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.TypeAliasParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.TypeSpecifierListParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.TypedefParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.environment.EnvironmentParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.event.EventParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.stream.StreamParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.trace.TraceDeclarationParser;
import org.eclipse.tracecompass.internal.ctf.core.trace.CTFStream;
import com.google.common.collect.Iterables;
/**
* IOStructGen
*/
public class IOStructGen {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
/**
* The trace
*/
private final @NonNull CTFTrace fTrace;
private CommonTree fTree;
/**
* The current declaration scope.
*/
private final @NonNull DeclarationScope fRoot;
/**
* Data helpers needed for streaming
*/
private boolean fHasBeenParsed = false;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructor
*
* @param tree
* the tree (ANTLR generated) with the parsed TSDL data.
* @param trace
* the trace containing the places to put all the read metadata
*/
public IOStructGen(CommonTree tree, @NonNull CTFTrace trace) {
fTrace = trace;
fTree = tree;
fRoot = NonNullUtils.checkNotNull(trace.getScope());
}
/**
* Parse the tree and populate the trace defined in the constructor.
*
* @throws ParseException
* If there was a problem parsing the metadata
*/
public void generate() throws ParseException {
parseRoot(fTree);
}
/**
* Parse a partial tree and populate the trace defined in the constructor.
* Does not check for a "trace" block as there is only one in the trace and
* thus
*
* @throws ParseException
* If there was a problem parsing the metadata
*/
public void generateFragment() throws ParseException {
parseIncompleteRoot(fTree);
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
/**
* Sets a new tree to parse
*
* @param newTree
* the new tree to parse
*/
public void setTree(CommonTree newTree) {
fTree = newTree;
}
/**
* Parse the root node.
*
* @param root
* A ROOT node.
* @throws ParseException
*/
private void parseRoot(CommonTree root) throws ParseException {
List<CommonTree> children = root.getChildren();
CommonTree traceNode = null;
boolean hasStreams = false;
List<CommonTree> events = new ArrayList<>();
for (CommonTree child : children) {
final int type = child.getType();
switch (type) {
case CTFParser.DECLARATION:
parseRootDeclaration(child);
break;
case CTFParser.TRACE:
if (traceNode != null) {
throw new ParseException("Only one trace block is allowed"); //$NON-NLS-1$
}
traceNode = child;
parseTrace(traceNode);
break;
case CTFParser.STREAM:
StreamParser.INSTANCE.parse(child, new StreamParser.Param(fTrace, fRoot));
hasStreams = true;
break;
case CTFParser.EVENT:
events.add(child);
break;
case CTFParser.CLOCK:
CTFClock ctfClock = ClockParser.INSTANCE.parse(child, null);
String nameValue = ctfClock.getName();
fTrace.addClock(nameValue, ctfClock);
break;
case CTFParser.ENV:
fTrace.setEnvironment(EnvironmentParser.INSTANCE.parse(child, null));
break;
default:
throw childTypeError(child);
}
}
if (traceNode == null) {
throw new ParseException("Missing trace block"); //$NON-NLS-1$
}
parseEvents(events, hasStreams);
fHasBeenParsed = true;
}
private void parseEvents(List<CommonTree> events, boolean hasStreams) throws ParseException {
if (!hasStreams && !events.isEmpty()) {
/* Add an empty stream that will have a null id */
fTrace.addStream(new CTFStream(fTrace));
}
for (CommonTree event : events) {
EventParser.INSTANCE.parse(event, new EventParser.Param(fTrace, fRoot));
}
}
private void parseIncompleteRoot(CommonTree root) throws ParseException {
if (!fHasBeenParsed) {
throw new ParseException("You need to run generate first"); //$NON-NLS-1$
}
List<CommonTree> children = root.getChildren();
List<CommonTree> events = new ArrayList<>();
for (CommonTree child : children) {
final int type = child.getType();
switch (type) {
case CTFParser.DECLARATION:
parseRootDeclaration(child);
break;
case CTFParser.TRACE:
throw new ParseException("Trace block defined here, please use generate and not generateFragment to parse this fragment"); //$NON-NLS-1$
case CTFParser.STREAM:
StreamParser.INSTANCE.parse(child, new StreamParser.Param(fTrace, fRoot));
break;
case CTFParser.EVENT:
events.add(child);
break;
case CTFParser.CLOCK:
CTFClock ctfClock = ClockParser.INSTANCE.parse(child, null);
String nameValue = ctfClock.getName();
fTrace.addClock(nameValue, ctfClock);
break;
case CTFParser.ENV:
fTrace.setEnvironment(EnvironmentParser.INSTANCE.parse(child, null));
break;
default:
throw childTypeError(child);
}
}
parseEvents(events, !Iterables.isEmpty(fTrace.getStreams()));
}
private void parseTrace(CommonTree traceNode) throws ParseException {
CTFTrace trace = fTrace;
List<CommonTree> children = traceNode.getChildren();
if (children == null) {
throw new ParseException("Trace block is empty"); //$NON-NLS-1$
}
for (CommonTree child : children) {
switch (child.getType()) {
case CTFParser.TYPEALIAS:
TypeAliasParser.INSTANCE.parse(child, new TypeAliasParser.Param(trace, fRoot));
break;
case CTFParser.TYPEDEF:
TypedefParser.INSTANCE.parse(child, new TypedefParser.Param(trace, fRoot));
break;
case CTFParser.CTF_EXPRESSION_TYPE:
case CTFParser.CTF_EXPRESSION_VAL:
TraceDeclarationParser.INSTANCE.parse(child, new TraceDeclarationParser.Param(fTrace, fRoot));
break;
default:
throw childTypeError(child);
}
}
/*
* If trace byte order was not specified and not using packet based
* metadata
*/
if (fTrace.getByteOrder() == null) {
throw new ParseException("Trace byte order not set"); //$NON-NLS-1$
}
}
/**
* Parses a declaration at the root level.
*
* @param declaration
* The declaration subtree.
* @throws ParseException
*/
private void parseRootDeclaration(CommonTree declaration)
throws ParseException {
List<CommonTree> children = declaration.getChildren();
for (CommonTree child : children) {
switch (child.getType()) {
case CTFParser.TYPEDEF:
TypedefParser.INSTANCE.parse(child, new TypedefParser.Param(fTrace, fRoot));
break;
case CTFParser.TYPEALIAS:
TypeAliasParser.INSTANCE.parse(child, new TypeAliasParser.Param(fTrace, fRoot));
break;
case CTFParser.TYPE_SPECIFIER_LIST:
TypeSpecifierListParser.INSTANCE.parse(child, new TypeSpecifierListParser.Param(fTrace, null, null, fRoot));
break;
default:
throw childTypeError(child);
}
}
}
/**
* Throws a ParseException stating that the parent-child relation between
* the given node and its parent is not valid. It means that the shape of
* the AST is unexpected.
*
* @param child
* The invalid child node.
* @return ParseException with details
*/
private static ParseException childTypeError(CommonTree child) {
CommonTree parent = (CommonTree) child.getParent();
String error = "Parent " + CTFParser.tokenNames[parent.getType()] //$NON-NLS-1$
+ " can't have a child of type " //$NON-NLS-1$
+ CTFParser.tokenNames[child.getType()] + "."; //$NON-NLS-1$
return new ParseException(error);
}
}