<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://performiq.com/kb/index.php?action=history&amp;feed=atom&amp;title=Impact_-_A_Replacement_FembicReader</id>
	<title>Impact - A Replacement FembicReader - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://performiq.com/kb/index.php?action=history&amp;feed=atom&amp;title=Impact_-_A_Replacement_FembicReader"/>
	<link rel="alternate" type="text/html" href="https://performiq.com/kb/index.php?title=Impact_-_A_Replacement_FembicReader&amp;action=history"/>
	<updated>2026-05-18T11:38:19Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>https://performiq.com/kb/index.php?title=Impact_-_A_Replacement_FembicReader&amp;diff=5482&amp;oldid=prev</id>
		<title>PeterHarding at 21:05, 7 December 2025</title>
		<link rel="alternate" type="text/html" href="https://performiq.com/kb/index.php?title=Impact_-_A_Replacement_FembicReader&amp;diff=5482&amp;oldid=prev"/>
		<updated>2025-12-07T21:05:11Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 07:05, 8 December 2025&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l800&quot;&gt;Line 800:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 800:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;[[Category:Impact]]&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;[[Category:Java]]&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;[[Category:Projects]]&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>PeterHarding</name></author>
	</entry>
	<entry>
		<id>https://performiq.com/kb/index.php?title=Impact_-_A_Replacement_FembicReader&amp;diff=5479&amp;oldid=prev</id>
		<title>PeterHarding at 21:03, 7 December 2025</title>
		<link rel="alternate" type="text/html" href="https://performiq.com/kb/index.php?title=Impact_-_A_Replacement_FembicReader&amp;diff=5479&amp;oldid=prev"/>
		<updated>2025-12-07T21:03:50Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 07:03, 8 December 2025&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l49&quot;&gt;Line 49:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 49:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;import run.Tracker;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;import run.Tracker;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;import run.Writer;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;import run.Writer;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;import uka.karmi.rmi.RemoteException;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;import uka.karmi.rmi.RemoteException;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>PeterHarding</name></author>
	</entry>
	<entry>
		<id>https://performiq.com/kb/index.php?title=Impact_-_A_Replacement_FembicReader&amp;diff=5478&amp;oldid=prev</id>
		<title>PeterHarding: Created page with &quot;= The Fembic Reader =   Here is prototype code for a replacement tp the FembicReader which uses java.nio and java.util.regex.   &lt;pre&gt; /*  * 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 AN...&quot;</title>
		<link rel="alternate" type="text/html" href="https://performiq.com/kb/index.php?title=Impact_-_A_Replacement_FembicReader&amp;diff=5478&amp;oldid=prev"/>
		<updated>2025-12-07T21:03:29Z</updated>

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