/*******************************************************************************
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package hr.fer.zemris.vhdllab.service.extractor.source;
import hr.fer.zemris.vhdllab.service.ci.CircuitInterface;
import hr.fer.zemris.vhdllab.service.ci.Port;
import hr.fer.zemris.vhdllab.service.ci.PortDirection;
import hr.fer.zemris.vhdllab.service.exception.CircuitInterfaceExtractionException;
import hr.fer.zemris.vhdllab.service.exception.DependencyExtractionException;
import hr.fer.zemris.vhdllab.service.exception.VhdlGenerationException;
import hr.fer.zemris.vhdllab.service.extractor.AbstractMetadataExtractor;
import hr.fer.zemris.vhdllab.service.result.Result;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
public class SourceMetadataExtractor extends AbstractMetadataExtractor {
/*
* Notice: this class was written only to satisfy a test class so code is a
* disaster. In future, this class should be implemented using lexical and
* syntactic analysis.
*/
// constants used in parsing
private static final String ENTITY = "ENTITY";
private static final String GENERIC = "GENERIC";
private static final String END = "END";
private static final String IS = "IS";
private static final String PORT = "PORT";
private static final String WHITESPACE = " ";
private static final String DOT = ".";
private static final String COLON = ":";
private static final String SEMICOLON = ";";
private static final String COMMA = ",";
private static final String LEFT_BRACKET = "(";
private static final String RIGHT_BRACKET = ")";
private static final String WORK = "WORK";
private static final String COMPONENT = "COMPONENT";
@Override
protected CircuitInterface doExtractCircuitInterface(String data)
throws CircuitInterfaceExtractionException {
String cleanedSource = data;
cleanedSource = VhdlUtil.decomment(cleanedSource);
cleanedSource = VhdlUtil.removeWhiteSpaces(cleanedSource);
return extractCi(cleanedSource);
}
private int next(String source, String keyword, int offset) {
if (!source.startsWith(keyword, offset)) {
throwException();
}
return offset + keyword.length();
}
private int maybeNext(String source, String keyword, int offset) {
int nextPosition = offset;
if (source.startsWith(keyword, offset)) {
nextPosition += keyword.length();
}
return nextPosition;
}
private int parsePorts(List<Port> ports, String source, String original,
int pos) {
int start = pos;
boolean exit = false;
while (true) {
int end = source.indexOf(SEMICOLON, start);
if (end == -1 || end == start) {
throwException();
}
int end2 = maybeNext(source, WHITESPACE, end + SEMICOLON.length());
if (source.startsWith(END, end2)
|| source.startsWith(GENERIC, end2)) {
if (source.startsWith(RIGHT_BRACKET + WHITESPACE, end - 2)) {
end -= 2;
} else if (source.startsWith(RIGHT_BRACKET, end - 1)) {
end -= 1;
}
exit = true;
}
parsePort(ports, source.substring(start, end), original.substring(
start, end));
if (exit) {
return end;
}
start = end + 1;
}
}
private void parsePort(List<Port> ports, String source, String original) {
int pos = source.indexOf(COLON);
if (pos == -1) {
throwException();
}
String portNames = original.substring(0, pos).trim();
pos += COLON.length();
int start = maybeNext(source, WHITESPACE, pos);
int end = source.indexOf(WHITESPACE, start);
if (end == -1) {
throwException();
}
String direction = source.substring(start, end).trim();
PortDirection portDirection = null;
try {
portDirection = PortDirection.valueOf(direction);
} catch (IllegalArgumentException e) {
throwException();
}
end += WHITESPACE.length();
Port type = new Port();
parseType(source.substring(end), type);
for (String n : portNames.split(COMMA)) {
n = n.trim();
Port port = new Port(type);
port.setDirection(portDirection);
port.setName(n);
ports.add(port);
}
}
private void parseType(String source, Port p) {
String s = source.trim();
if (s.equals("STD_LOGIC")) {
return;
}
if (!s.startsWith("STD_LOGIC_VECTOR")) {
throwException();
}
int pos = "STD_LOGIC_VECTOR".length();
pos = maybeNext(s, WHITESPACE, pos);
int start = next(s, LEFT_BRACKET, pos);
start = maybeNext(s, WHITESPACE, start);
int end = s.indexOf(WHITESPACE, start);
if (end == -1) {
throwException();
}
String fromString = s.substring(start, end);
end += WHITESPACE.length();
int from = 0;
try {
from = Integer.parseInt(fromString);
} catch (NumberFormatException e) {
throwException();
}
start = end;
end = s.indexOf(WHITESPACE, start);
if (end == -1) {
throwException();
}
// vector direction here that we omit
end += WHITESPACE.length();
start = end;
end = s.indexOf(RIGHT_BRACKET, start);
if (end < start) {
throwException();
}
String toString = s.substring(start, end).trim();
end += RIGHT_BRACKET.length();
int to = 0;
try {
to = Integer.parseInt(toString);
} catch (NumberFormatException e) {
throwException();
}
end = maybeNext(s, WHITESPACE, end);
if (end != s.length()) {
throwException();
}
p.setFrom(from);
p.setTo(to);
}
private CircuitInterface extractCi(String original) {
List<Port> ports = new ArrayList<Port>();
String source = original.toUpperCase(Locale.ENGLISH);
int pos = source.indexOf(ENTITY);
if (pos == -1) {
throwException();
}
pos = next(source, ENTITY, pos);
pos = next(source, WHITESPACE, pos);
int start = pos;
int end = source.indexOf(WHITESPACE, start);
String entityName = original.substring(start, end);
pos = end + WHITESPACE.length();
pos = next(source, IS, pos);
pos = next(source, WHITESPACE, pos);
if (source.startsWith(PORT, pos)) {
pos = port(ports, source, original, pos);
if (source.startsWith(GENERIC, pos)) {
pos = generic(source, pos);
}
} else if (source.startsWith(GENERIC, pos)) {
pos = generic(source, pos);
if (source.startsWith(PORT, pos)) {
pos = port(ports, source, original, pos);
}
}
pos = next(source, END, pos);
pos = next(source, WHITESPACE, pos);
if (source.startsWith(ENTITY, pos)) {
pos += ENTITY.length();
pos = next(source, WHITESPACE, pos);
}
pos = next(source, entityName.toUpperCase(Locale.ENGLISH), pos);
pos = maybeNext(source, WHITESPACE, pos);
pos = next(source, SEMICOLON, pos);
// check for duplicate entity block
while((pos = source.indexOf(ENTITY, pos)) != -1) {
pos = next(source, ENTITY, pos);
if(source.startsWith(WHITESPACE, pos)) {
pos += WHITESPACE.length();
int marker = pos;
while(pos < source.length()) {
char c = source.charAt(pos);
if(Character.isLetter(c) || Character.isDigit(c) || c=='_' || (pos>marker && c=='.')) {
pos++;
} else {
break;
}
}
if(pos-marker<1) { // ako iza entity ne ide identifikator, vozi dalje...
continue;
}
String label = source.substring(marker, pos);
if(label.contains(".")) { // hvata nazive oblika library.sklop, npr. WORK.sklop1
continue;
}
if(source.startsWith(WHITESPACE, pos)) {
pos += WHITESPACE.length();
}
if(source.startsWith(";", pos)) continue;
throwException("Duplicate entity block");
}
}
try {
CircuitInterface ci = new CircuitInterface(entityName);
ci.addAll(ports);
return ci;
} catch (IllegalArgumentException e) {
throwException();
return null;
}
}
private int generic(String source, int p) {
int pos = p + GENERIC.length();
pos = maybeNext(source, WHITESPACE, pos);
pos = next(source, LEFT_BRACKET, pos);
pos = source.indexOf(RIGHT_BRACKET, pos);
if (pos == -1) {
throwException();
}
pos += RIGHT_BRACKET.length();
pos = maybeNext(source, WHITESPACE, pos);
pos = next(source, SEMICOLON, pos);
pos = maybeNext(source, WHITESPACE, pos);
return pos;
}
private int port(List<Port> ports, String source, String original, int p) {
int pos = p + PORT.length();
pos = maybeNext(source, WHITESPACE, pos);
pos = next(source, LEFT_BRACKET, pos);
pos = maybeNext(source, WHITESPACE, pos);
pos = parsePorts(ports, source, original, pos);
pos = maybeNext(source, WHITESPACE, pos);
pos = next(source, RIGHT_BRACKET, pos);
pos = maybeNext(source, WHITESPACE, pos);
pos = next(source, SEMICOLON, pos);
pos = maybeNext(source, WHITESPACE, pos);
return pos;
}
private void throwException() {
throwException("Entity block is invalid.");
}
private void throwException(String message) {
throw new CircuitInterfaceExtractionException(message);
}
@Override
protected Set<String> doExtractDependencies(String data)
throws DependencyExtractionException {
String source = data;
source = VhdlUtil.decomment(source);
source = VhdlUtil.removeWhiteSpaces(source);
return extract(source);
}
private Set<String> extract(String original) {
Set<String> dependencies = new HashSet<String>();
Set<String> depsOrigCase = new HashSet<String>();
String source = original.toUpperCase(Locale.ENGLISH);
int pos = 0;
while (true) {
pos = source.indexOf(ENTITY, pos);
if (pos == -1) {
break;
}
pos += ENTITY.length();
if (!source.startsWith(WHITESPACE, pos)) {
continue;
}
pos += WHITESPACE.length();
if (!source.startsWith(WORK, pos)) {
continue;
}
pos += WORK.length();
pos = maybeNext(source, WHITESPACE, pos);
if (!source.startsWith(DOT, pos)) {
continue;
}
pos += DOT.length();
pos = maybeNext(source, WHITESPACE, pos);
int start = pos;
pos = source.indexOf(WHITESPACE, start);
if (pos == -1) {
pos = start;
continue;
}
String component = original.substring(start, pos);
String cmpIgnoreCase = component.toLowerCase();
if (!dependencies.contains(cmpIgnoreCase)) {
dependencies.add(cmpIgnoreCase);
depsOrigCase.add(component);
}
}
pos = 0;
while (true) {
pos = source.indexOf(COMPONENT, pos);
if (pos == -1) {
break;
}
pos += COMPONENT.length();
if (!source.startsWith(WHITESPACE, pos)) {
continue;
}
pos += WHITESPACE.length();
if (source.startsWith(SEMICOLON, pos)) {
continue;
}
int start = pos;
pos = source.indexOf(WHITESPACE, start);
if (pos == -1) {
pos = start;
continue;
}
String component = original.substring(start, pos);
String cmpIgnoreCase = component.toLowerCase();
if(component.contains(";")) continue; // ako sam uhvatio "END COMPONENT sklop2;"
if (!dependencies.contains(cmpIgnoreCase)) {
dependencies.add(cmpIgnoreCase);
depsOrigCase.add(component);
}
}
return depsOrigCase;
}
@Override
protected Result doGenerateVhdl(String data) throws VhdlGenerationException {
return new Result(data);
}
}