Impact - A Replacement FembicReader
Jump to navigation
Jump to search
The Fembic Reader
Here is prototype code for a replacement tp the FembicReader which uses java.nio and java.util.regex.
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, inc., 59 Temple Place, Suite 330, Boston MA 02111-1307
* USA
*/
package run.readers;
// ----------------------------------------------------------------------
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// ----------------------------------------------------------------------
import run.Constraint;
import run.Controlset;
import run.Element;
import run.Load;
import run.Material;
import run.Node;
import run.Reader;
import run.RplVector;
import run.Token;
import run.TrackWriter;
import run.Tracker;
import run.Writer;
import uka.karmi.rmi.RemoteException;
import jp.lang.RemoteObject;
// ----------------------------------------------------------------------
/**
* Fembic file reader - modernized version using BufferedReader and regex.
* Original authors: Yuriy Mikhaylovskiy, Jonas Forssell
* Modernized: 2025
*/
public class FembicReader extends Reader {
private BufferedReader br;
private final String filename;
private final Set<String> keywords = Set.of(
"TITLE", "CONTROLS", "ELEMENTS", "NODES", "LOADS",
"CONSTRAINTS", "MATERIALS", "TRACKERS", "GROUPS", "GEOMETRY"
);
// Parsing state
private boolean inBlock = false;
private boolean elementInBlock = false;
private boolean trackerInBlock = false;
private boolean materialInBlock = false;
private boolean controlInBlock = false;
private boolean constraintInBlock = false;
// Current type being parsed
private String currentElementType;
private String currentTrackerType;
private String currentConstraintType;
// Line-based parsing state
private List<String> fileLines;
private int currentLineIndex;
private int currentLineNumber;
// Regex patterns for parsing
private static final Pattern BLOCK_HEADER_PATTERN =
Pattern.compile("^(\\w+)\\s+OF\\s+TYPE\\s+(\\w+)\\s*$", Pattern.CASE_INSENSITIVE);
private static final Pattern SIMPLE_BLOCK_PATTERN =
Pattern.compile("^(\\w+)\\s*$", Pattern.CASE_INSENSITIVE);
private static final Pattern NUMBER_PATTERN =
Pattern.compile("^-?\\d+(\\.\\d+)?([eE][+-]?\\d+)?$");
private static final Pattern PARAM_EQUALS_PATTERN =
Pattern.compile("(\\w+)\\s*=\\s*(.+)");
// Temporary objects
private Node temporaryNode;
private Load temporaryLoad;
/**
* Constructor - stores filename for later opening.
*/
public FembicReader(String fn) {
this.filename = fn;
}
/**
* Closes the reader.
*/
public void close() {
if (br != null) {
try {
br.close();
} catch (IOException ioe) {
System.out.println("An IOException has occurred when closing the indata file");
}
}
fileLines = null;
currentLineIndex = 0;
}
/**
* Opens the file and loads all lines into memory for parsing.
*/
public void open() {
// Reset parsing state
inBlock = false;
controlInBlock = false;
constraintInBlock = false;
elementInBlock = false;
trackerInBlock = false;
materialInBlock = false;
currentLineIndex = 0;
try {
Path path = Path.of(filename);
fileLines = Files.readAllLines(path);
br = Files.newBufferedReader(path);
} catch (IOException e) {
System.out.println("Failed to open Fembic file: " + e.getMessage());
fileLines = new ArrayList<>();
}
}
/**
* Checks if a string is a keyword.
*/
private boolean isAKeyword(String param) {
return keywords.contains(param.toUpperCase());
}
/**
* Gets the next non-empty, non-comment line.
* Returns null if EOF reached.
*/
private String getNextLine() {
while (currentLineIndex < fileLines.size()) {
String line = fileLines.get(currentLineIndex).trim();
currentLineNumber = currentLineIndex + 1;
currentLineIndex++;
// Skip empty lines and comments
if (!line.isEmpty() && !line.startsWith("#") && !line.startsWith("//")) {
return line;
}
}
return null;
}
/**
* Peeks at the next non-empty line without consuming it.
*/
private String peekNextLine() {
int tempIndex = currentLineIndex;
while (tempIndex < fileLines.size()) {
String line = fileLines.get(tempIndex).trim();
tempIndex++;
if (!line.isEmpty() && !line.startsWith("#") && !line.startsWith("//")) {
return line;
}
}
return null;
}
/**
* Pushes back to re-read the current line.
*/
private void pushBack() {
if (currentLineIndex > 0) {
currentLineIndex--;
}
}
/**
* Searches for a block with the given keyword.
* Returns true if found, false if EOF reached.
*/
private boolean findBlock(String blockKeyword) {
String line;
while ((line = getNextLine()) != null) {
String upperLine = line.toUpperCase();
if (upperLine.startsWith(blockKeyword.toUpperCase())) {
return true;
}
}
return false;
}
/**
* Searches for a typed block (e.g., "ELEMENTS OF TYPE BEAM").
* Returns the type if found, null otherwise.
*/
private String findTypedBlock(String blockKeyword) {
String line;
while ((line = getNextLine()) != null) {
Matcher matcher = BLOCK_HEADER_PATTERN.matcher(line);
if (matcher.matches() && matcher.group(1).equalsIgnoreCase(blockKeyword)) {
return matcher.group(2).toUpperCase();
}
}
return null;
}
/**
* Checks if a string represents a number.
*/
private boolean isNumber(String s) {
return NUMBER_PATTERN.matcher(s).matches();
}
/**
* Parses a number from a string.
*/
private double parseNumber(String s) {
return Double.parseDouble(s);
}
/**
* Tokenizes a line into Token array for parsing.
* Handles parameter sets in brackets and equals signs.
*/
public Token[] tokenize(String line) {
List<Token> tokens = new ArrayList<>();
// Split on whitespace but preserve bracket contents
List<String> parts = splitPreservingBrackets(line);
for (String part : parts) {
part = part.trim();
if (part.isEmpty()) continue;
// Handle bracket sets [x,y,z]
if (part.startsWith("[") && part.endsWith("]")) {
tokens.add(new Token(part));
continue;
}
// Handle param=value (no spaces around =)
if (part.contains("=") && part.length() > 1) {
int eqIndex = part.indexOf("=");
if (eqIndex > 0) {
tokens.add(new Token(part.substring(0, eqIndex)));
}
tokens.add(new Token("="));
if (eqIndex < part.length() - 1) {
String value = part.substring(eqIndex + 1);
addValueToken(tokens, value);
}
continue;
}
// Regular token
addValueToken(tokens, part);
}
return tokens.toArray(new Token[0]);
}
/**
* Splits a string on whitespace while preserving content within brackets.
*/
private List<String> splitPreservingBrackets(String line) {
List<String> parts = new ArrayList<>();
StringBuilder current = new StringBuilder();
int bracketDepth = 0;
for (char c : line.toCharArray()) {
if (c == '[') {
bracketDepth++;
current.append(c);
} else if (c == ']') {
bracketDepth--;
current.append(c);
} else if (Character.isWhitespace(c) && bracketDepth == 0) {
if (current.length() > 0) {
parts.add(current.toString());
current = new StringBuilder();
}
} else {
current.append(c);
}
}
if (current.length() > 0) {
parts.add(current.toString());
}
return parts;
}
/**
* Adds a token, determining if it's a number or word.
*/
private void addValueToken(List<Token> tokens, String value) {
if (isNumber(value)) {
tokens.add(new Token(parseNumber(value)));
} else {
tokens.add(new Token(value));
}
}
/**
* Reads and parses the control set.
*/
public void getControlSet(Controlset temporaryControlset) throws IllegalArgumentException {
try {
if (!controlInBlock) {
if (!findBlock("CONTROLS")) {
throw new ParseException("No controls block found or missing controls", currentLineNumber);
}
controlInBlock = true;
}
// Read control lines until we hit a keyword
String line;
while ((line = peekNextLine()) != null) {
String[] parts = line.split("\\s+");
if (parts.length > 0 && isAKeyword(parts[0])) {
break;
}
line = getNextLine();
if (line != null && !line.isEmpty()) {
temporaryControlset.parse_Fembic(tokenize(line), currentLineNumber);
}
}
temporaryControlset.checkIndata();
} catch (ParseException e) {
throw new IllegalArgumentException("Failed to generate control set: " + e.getMessage());
}
}
/**
* Reads the next element from the file.
*/
@SuppressWarnings("rawtypes")
public Element getNextElement(RplVector materiallist, RplVector nodelist,
RplVector loadlist, Hashtable nodetable) throws ParseException {
try {
// Find next element block if needed
String line = peekNextLine();
if (line == null) {
throw new ParseException("No elements block found or missing elements", currentLineNumber);
}
// Check if we need to find a new element block
String[] parts = line.split("\\s+");
if (!elementInBlock || (parts.length > 0 && !isNumber(parts[0]))) {
// Search for next ELEMENTS OF TYPE block
String type = findTypedBlock("ELEMENTS");
if (type == null) {
throw new ParseException("No elements block found or missing elements", currentLineNumber);
}
currentElementType = type;
elementInBlock = true;
System.out.println("Element block found!");
}
// Read the element line
line = getNextLine();
if (line == null) {
throw new ParseException("Unexpected end of file while reading elements", currentLineNumber);
}
parts = line.split("\\s+", 2);
if (parts.length < 1 || !isNumber(parts[0])) {
throw new ParseException("Expected element number", currentLineNumber);
}
int elementNumber = (int) parseNumber(parts[0]);
String restOfLine = parts.length > 1 ? parts[1] : "";
Element temporaryElement = Element.getElementOfType_Fembic(currentElementType);
temporaryElement.setNumber(elementNumber);
temporaryElement.parse_Fembic(tokenize(restOfLine), currentLineNumber,
nodelist, materiallist, loadlist, nodetable);
temporaryElement.checkIndata();
return temporaryElement;
} catch (ParseException e) {
throw new IllegalArgumentException(e.getMessage() + " in line: " + currentLineNumber);
}
}
/**
* Reads the next tracker from the file.
*/
public Tracker getNextTracker(RplVector nodelist, RplVector elementlist) throws ParseException {
try {
String line = peekNextLine();
if (line == null) {
throw new ParseException("No trackers block found or missing trackers", currentLineNumber);
}
String[] parts = line.split("\\s+");
if (!trackerInBlock || (parts.length > 0 && !isNumber(parts[0]))) {
String type = findTypedBlock("TRACKERS");
if (type == null) {
throw new ParseException("No trackers block found or missing trackers", currentLineNumber);
}
currentTrackerType = type;
trackerInBlock = true;
System.out.println("Tracker block found!");
}
line = getNextLine();
if (line == null) {
throw new ParseException("Unexpected end of file while reading trackers", currentLineNumber);
}
parts = line.split("\\s+", 2);
if (parts.length < 1 || !isNumber(parts[0])) {
throw new ParseException("Expected tracker number", currentLineNumber);
}
int trackerNumber = (int) parseNumber(parts[0]);
String restOfLine = parts.length > 1 ? parts[1] : "";
Tracker temporaryTracker = Tracker.getTrackerOfType_Fembic(currentTrackerType);
temporaryTracker.setNumber(trackerNumber);
temporaryTracker.parse_Fembic(tokenize(restOfLine), currentLineNumber, nodelist, elementlist);
temporaryTracker.checkIndata();
return temporaryTracker;
} catch (ParseException e) {
throw new IllegalArgumentException(e.getMessage() + " in line: " + currentLineNumber);
}
}
/**
* Reads the next constraint from the file.
*/
public Constraint getNextConstraint(RplVector nodelist) throws ParseException {
try {
String line = peekNextLine();
if (line == null) {
throw new ParseException("No constraint block found or missing constraints", currentLineNumber);
}
String[] parts = line.split("\\s+");
if (!constraintInBlock || (parts.length > 0 && isAKeyword(parts[0]))) {
String type = findTypedBlock("CONSTRAINTS");
if (type == null) {
throw new ParseException("No constraint block found or missing constraints", currentLineNumber);
}
currentConstraintType = type;
constraintInBlock = true;
System.out.println("Constraint block found!");
}
line = getNextLine();
if (line == null) {
throw new ParseException("Unexpected end of file while reading constraints", currentLineNumber);
}
parts = line.split("\\s+", 2);
if (parts.length < 1) {
throw new ParseException("Expected constraint name", currentLineNumber);
}
String constraintName = parts[0].toUpperCase().trim();
String restOfLine = parts.length > 1 ? parts[1] : "";
Constraint temporaryConstraint = Constraint.getConstraintOfType_Fembic(currentConstraintType);
temporaryConstraint.setName(constraintName);
temporaryConstraint.parse_Fembic(tokenize(restOfLine), currentLineNumber, nodelist);
temporaryConstraint.checkIndata();
return temporaryConstraint;
} catch (ParseException e) {
throw new IllegalArgumentException(e.getMessage() + " in line: " + currentLineNumber);
}
}
/**
* Reads the next load from the file.
*/
public Load getNextLoad(RplVector nodelist) throws ParseException {
try {
if (!inBlock) {
if (!findBlock("LOADS")) {
throw new ParseException("No loads block found or missing loads", currentLineNumber);
}
inBlock = true;
}
String line = getNextLine();
if (line == null) {
throw new ParseException("Unexpected end of file while reading loads", currentLineNumber);
}
String[] parts = line.split("\\s+", 2);
if (parts.length < 1) {
throw new ParseException("No load name found", currentLineNumber);
}
temporaryLoad = new Load();
temporaryLoad.name = parts[0].toUpperCase();
String restOfLine = parts.length > 1 ? parts[1] : "";
temporaryLoad.parse_Fembic(tokenize(restOfLine), currentLineNumber);
temporaryLoad.checkIndata();
return temporaryLoad;
} catch (ParseException e) {
throw new IllegalArgumentException(e.getMessage() + " in line: " + currentLineNumber);
}
}
/**
* Reads the next material from the file.
*/
public Material getNextMaterial() throws ParseException {
try {
String line = peekNextLine();
if (line == null) {
throw new ParseException("No materials block found or missing materials", currentLineNumber);
}
String[] parts = line.split("\\s+");
if (!materialInBlock || (parts.length > 0 && isAKeyword(parts[0]))) {
String type = findTypedBlock("MATERIALS");
if (type == null) {
throw new ParseException("No materials block found or missing materials", currentLineNumber);
}
currentElementType = type; // reusing for material type
materialInBlock = true;
System.out.println("Block found!");
}
line = getNextLine();
if (line == null) {
throw new ParseException("Unexpected end of file while reading materials", currentLineNumber);
}
parts = line.split("\\s+", 2);
if (parts.length < 1) {
throw new ParseException("Expected material name", currentLineNumber);
}
String materialName = parts[0].toUpperCase();
String restOfLine = parts.length > 1 ? parts[1] : "";
Material temporaryMaterial = Material.getMaterialOfType_Fembic(currentElementType);
temporaryMaterial.setName(materialName);
temporaryMaterial.parse_Fembic(tokenize(restOfLine), currentLineNumber);
temporaryMaterial.checkIndata();
return temporaryMaterial;
} catch (ParseException e) {
throw new IllegalArgumentException(e.getMessage() + " in line: " + currentLineNumber);
}
}
/**
* Reads the next node from the file.
*/
public Node getNextNode(RplVector constraintlist, RplVector loadlist) throws ParseException {
try {
String line = peekNextLine();
if (line == null) {
throw new ParseException("No nodes block found or missing nodes", currentLineNumber);
}
String[] parts = line.split("\\s+");
if (!inBlock || (parts.length > 0 && !isNumber(parts[0]))) {
if (!findBlock("NODES")) {
throw new ParseException("No nodes block found or missing nodes", currentLineNumber);
}
inBlock = true;
}
line = getNextLine();
if (line == null) {
throw new ParseException("Unexpected end of file while reading nodes", currentLineNumber);
}
parts = line.split("\\s+", 2);
if (parts.length < 1 || !isNumber(parts[0])) {
throw new ParseException("Expected node number", currentLineNumber);
}
int nodeNumber = (int) parseNumber(parts[0]);
String restOfLine = parts.length > 1 ? parts[1] : "";
temporaryNode = new Node();
temporaryNode.setNumber(nodeNumber);
temporaryNode.parse_Fembic(tokenize(restOfLine), currentLineNumber, constraintlist, loadlist);
temporaryNode.checkIndata();
return temporaryNode;
} catch (ParseException e) {
throw new IllegalArgumentException(e.getMessage() + " in line: " + currentLineNumber);
}
}
// ========== Count Methods ==========
/**
* Counts items in a simple block (keyword followed by lines).
*/
private int countInSimpleBlock(String blockKeyword, boolean expectNumber) throws ParseException {
int count = 0;
open();
try {
for (String line : fileLines) {
line = line.trim();
if (line.isEmpty()) continue;
String upperLine = line.toUpperCase();
if (upperLine.startsWith(blockKeyword.toUpperCase())) {
// Check it's just the keyword (simple block)
Matcher simpleMatcher = SIMPLE_BLOCK_PATTERN.matcher(line);
if (simpleMatcher.matches()) {
// Found the block, now count items
int idx = fileLines.indexOf(line) + 1;
while (idx < fileLines.size()) {
String itemLine = fileLines.get(idx).trim();
idx++;
if (itemLine.isEmpty()) continue;
String[] parts = itemLine.split("\\s+");
if (parts.length > 0 && isAKeyword(parts[0])) {
break;
}
if (expectNumber) {
if (isNumber(parts[0])) {
count++;
}
} else {
if (!isAKeyword(parts[0])) {
count++;
}
}
}
}
}
}
} finally {
close();
}
System.out.println("Found " + count + " " + blockKeyword.toLowerCase() + "s");
return count;
}
/**
* Counts items in a typed block (keyword OF TYPE xxx).
*/
private int countInTypedBlock(String blockKeyword, boolean expectNumber) throws ParseException {
int count = 0;
open();
try {
for (int i = 0; i < fileLines.size(); i++) {
String line = fileLines.get(i).trim();
if (line.isEmpty()) continue;
Matcher matcher = BLOCK_HEADER_PATTERN.matcher(line);
if (matcher.matches() && matcher.group(1).equalsIgnoreCase(blockKeyword)) {
// Found a typed block, count items
int idx = i + 1;
while (idx < fileLines.size()) {
String itemLine = fileLines.get(idx).trim();
idx++;
if (itemLine.isEmpty()) continue;
String[] parts = itemLine.split("\\s+");
if (parts.length > 0 && isAKeyword(parts[0])) {
break;
}
if (expectNumber) {
if (isNumber(parts[0])) {
count++;
}
} else {
count++;
}
}
}
}
} finally {
close();
}
System.out.println("Found " + count + " " + blockKeyword.toLowerCase() + "s");
return count;
}
public int numberOfControls() throws ParseException {
int count = countInSimpleBlock("CONTROLS", false);
if (count == 0) {
throw new ParseException("No controls found", 0);
}
return count;
}
public int numberOfNodes() throws ParseException {
int count = countInSimpleBlock("NODES", true);
if (count == 0) {
throw new ParseException("No nodes found", 0);
}
return count;
}
public int numberOfLoads() throws ParseException {
return countInSimpleBlock("LOADS", false);
}
public int numberOfGroups() throws ParseException {
return countInSimpleBlock("GROUPS", false);
}
public int numberOfConstraints() throws ParseException {
return countInTypedBlock("CONSTRAINTS", false);
}
public int numberOfElements() throws ParseException {
int count = countInTypedBlock("ELEMENTS", true);
if (count == 0) {
throw new ParseException("No elements found", 0);
}
return count;
}
public int numberOfTrackers() throws ParseException {
return countInTypedBlock("TRACKERS", true);
}
public int numberOfMaterials() throws ParseException {
int count = countInTypedBlock("MATERIALS", false);
if (count == 0) {
throw new ParseException("No materials found", 0);
}
return count;
}
// ========== Writer Methods ==========
public Writer getWriter(RplVector nodelist, RplVector elementlist,
Controlset control, RemoteObject[] cluster_nodes) throws RemoteException {
Writer writer = Writer.getWriterOfType_Fembic(control.getWriter(), nodelist,
elementlist, cluster_nodes);
writer.checkIndata();
return writer;
}
public TrackWriter getTrackWriter(RplVector trackerlist,
Controlset control, RemoteObject[] cluster_nodes) throws RemoteException {
TrackWriter trackWriter = TrackWriter.getTrackWriterOfType_Fembic(
control.getTrackWriter(), trackerlist);
trackWriter.checkIndata();
return trackWriter;
}
/**
* Pre-processing hook (currently empty).
*/
public void preProcess() throws ParseException {
// No preprocessing needed
}
}