//---------------------------------------------------------------------------- // COMPONENT NAME: LPEX Editor // // © Copyright IBM Corporation 1998, 2008 // All Rights Reserved. // // DESCRIPTION: // Lpex - sample stand-alone SWT LPEX widget editor //---------------------------------------------------------------------------- package com.ibm.lpex.samples; import java.io.File; import java.util.ArrayList; import com.ibm.lpex.core.LpexAction; import com.ibm.lpex.core.LpexCommand; import com.ibm.lpex.core.LpexMessageConstants; import com.ibm.lpex.core.LpexResources; import com.ibm.lpex.core.LpexStringTokenizer; import com.ibm.lpex.core.LpexView; import com.ibm.lpex.core.LpexViewAdapter; import com.ibm.lpex.core.LpexWindow; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; /** * Sample stand-alone editor built on the LPEX edit widget. * * <p>Here is the Lpex <a href="doc-files/Lpex.java.html">source code</a>.</p> * * <p>Syntax for running Lpex from the command line: * <pre> * java [<i>java options</i>] com.ibm.lpex.samples.Lpex [<i>filename</i>] * [-encoding <i>charEncoding</i>] [-dt {<i>documentType</i> | none}] </pre> * For example: * <pre> * java com.ibm.lpex.samples.Lpex \sample.props -dt properties </pre> * A possible Windows batch program is: * <pre> * @start /b javaw com.ibm.lpex.samples.Lpex %1 %2 %3 %4 %5 %6 %7 %8 %9 </pre> * (use <code>java</code> instead of <code>javaw</code> to see the stack trace for exceptions). * To disable the JIT compiler, run it with this java option: * <pre> * -Djava.compiler= </pre> * You can run Lpex in a particular locale. For example, in order to run it in * Simplified Chinese (zh_CN), use these two java options: * <pre> * -Duser.language=zh -Duser.region=CN </pre></p> * * Commands and actions defined in here: * <ul> * <li><b>Lpex</b>, <b>e</b> commands - open a new Lpex window * <li><b>exit</b> command - close all Lpex views of the document * <li><b>openNewView</b> command - open an additional view on the document * <li><b>quit</b> command - unconditionally quit the Lpex window * <li><b>test</b> command - placeholder for testing new commands * <li><b>view</b> { log | profile } command - view the LPEX editor log / defaults profile * <li><b>close</b> action (F3) - close the Lpex window * <li><b>nextView</b> action (Alt+Shift+->) - go to the next view of this document * <li><b>openNewView</b> action (Ctrl+O) - open an additional view on the document * <li><b>prevView</b> action (Alt+Shift+<-) - go to the previous view of this document * <li><b>test</b> action - placeholder for testing new actions. * </ul> * * <p>Example user-defined editor action to open an additional document view with Lpex:</p> * <table bgcolor="#f0f0f0"><tr><td><pre> * lpexView.defineAction(<font color="#800080">"openNewView"</font>, <font color="#0000ff"><b>new</b></font> LpexAction() { * <font color="#0000ff"><b>public void</b></font> doAction(LpexView view) * { * Display display = getDisplay(); * display.asyncExec(<font color="#0000ff"><b>new</b></font> Runnable() { * <font color="#0000ff"><b>public void</b></font> run() { * <font color="#0000ff"><b>try</b></font> * { * Class cl = Class.forName(<font color="#800080">"com.ibm.lpex.samples.Lpex"</font>); * Constructor lpexConstructor = cl.getConstructor(<font color="#0000ff"><b>new</b></font> Class[] * { LpexView.class, com.ibm.lpex.samples.Lpex.Delegate.class, * Rectangle.class, Boolean.TYPE }); * lpexConstructor.newInstance(<font color="#0000ff"><b>new</b></font> Object[] * { <i>getView</i>(), <i>getDelegate</i>(), <font color="#0000ff"><b>new</b></font> Rectangle(10, 10, 648, 711), * Boolean.valueOf(<font color="#0000ff"><b>false</b></font>) }); * } * <font color="#0000ff"><b>catch</b></font>(Exception e) {} * }}); * } * <font color="#0000ff"><b>public boolean</b></font> available(LpexView view) * { <font color="#0000ff"><b>return true</b></font>; } * }); </pre></td></tr></table> * * @see com.ibm.lpex.samples All the samples */ public final class Lpex { private Shell _shell; // our shell private boolean _browse; // opened as read only private LpexWindow _lpexWindow; private LpexView _lpexView; LpexViewNode _lpexViewNode; private Shell _papaShell; private Listener _papaShellListener; /** * Entry point. */ public static void main(String[] args) { new Lpex(args, null /*bounds*/, false /*browse*/, null /*papaShell*/); } /** * Constructor for opening a file. * @param parms file name and parameters * @param bounds size and position for the window * @param browse if <code>true</code>, enforce read-only mode * @param papaShell Shell of the external program that started us, or <code>null</code> */ public Lpex(String[] parms, Rectangle bounds, boolean browse, Shell papaShell) { // extract file name and parameters LpexParameters p = new LpexParameters(parms); String filename = p.filename(); // ensure canonical file name if (filename != null) { if (filename.trim().length() == 0) { filename = null; } else { try { filename = (new File(filename)).getCanonicalPath(); } catch(Exception e) {} } } // create an LpexView for the given file name and character encoding, _lpexView = new LpexView(filename, p.encoding(), false); // do "updateProfile" later _lpexViewNode = new LpexViewNode(this); // determine document parser to use if (p.documentType() != null) { if ("none".equals(p.documentType())) { _lpexView.doCommand("set updateProfile.noParser on"); } else { _lpexView.doCommand("set updateProfile.parser " + _lpexView.query("current.updateProfile.parserAssociation." + p.documentType())); } } // opened from an external program, asked to listen to its demise if (papaShell != null) { _papaShell = papaShell; _papaShellListener = new Listener() { public void handleEvent(Event event) { if (event.type == SWT.Close) { if (_lpexView.queryInt("changes") == 0 && !_lpexView.queryOn("dirty")) { _lpexView.doCommand("exit"); // clean doc - close immediately } else if (_shell != null) { _shell.getDisplay().asyncExec(new Runnable() { public void run() { if (_lpexView != null) { _lpexView.doCommand("exit"); // changes pending - must prompt user } }}); } } else if (event.type == SWT.Dispose) { _papaShell = null; _papaShellListener = null; } }}; _papaShell.addListener(SWT.Close, _papaShellListener); _papaShell.addListener(SWT.Dispose, _papaShellListener); } // initialize the view setUpView(bounds, browse, null); } /** * Constructor for an external program to create a new (secondary) view on its * existing document. * @param lpexView master LpexView of the document * @param delegate optional Delegate object for the external program to handle * actions and commands from this Lpex view * @param bounds size and position for the window * @param browse if <code>true</code>, enforce read-only mode */ public Lpex(LpexView lpexView, Delegate delegate, Rectangle bounds, boolean browse) throws LpexView.ViewInstantiationException { this(LpexViewNode.getLpexViewNode(lpexView, delegate), bounds, browse); } /** * Constructor for a new view on an existing document. * @param lpexViewNode an existing view on the document * @param bounds size and position for the window * @param browse if <code>true</code>, enforce read-only mode */ private Lpex(LpexViewNode lpexViewNode, Rectangle bounds, boolean browse) throws LpexView.ViewInstantiationException { // originating view LpexView lpexView = lpexViewNode.lpexView(); // create a new LpexView for the given view's document, // do an explicit "updateProfile" later _lpexView = new LpexView(lpexView, false); _lpexViewNode = new LpexViewNode(this, lpexViewNode); // initialize the view setUpView(bounds, browse, lpexView); } /** * Initializes the new Lpex view. * @param bounds size and position for the window * @param browse if <code>true</code>, enforce read-only mode * @param fromView optional document view from which this Lpex was started */ private void setUpView(Rectangle bounds, boolean browse, LpexView fromView) { _browse = browse; // create a new Shell _shell = new Shell(); // use the same parser as the originating view if (fromView != null) { if (fromView.queryOn("updateProfile.noParser")) { _lpexView.doCommand("set updateProfile.noParser on"); } else { _lpexView.doCommand("set updateProfile.parser " + fromView.query("updateProfile.parser")); } } // create & set a window for our LpexView _lpexWindow = new LpexWindow(_shell); _lpexView.setWindow(_lpexWindow); // add an LpexViewListener to handle all the LPEX listening needed here _lpexView.addLpexViewListener(new LpexViewAdapter() { public void renamed(LpexView lpexView) { Lpex.this.renamed(lpexView); } public void updateProfile(LpexView lpexView) { Lpex.this.updateProfile(lpexView); } public void disposed(LpexView lpexView) { _lpexViewNode.remove(); } }); // run the "updateProfile" command _lpexView.doDefaultCommand("updateProfile"); // for a regular document with an underlying file, if file is currently // read only listen to the user changing its attribute to read/write if (_lpexView.queryOn("readonly")) { if (!_browse && hasUnderlyingFile()) { _lpexView.window().textWindow().addFocusListener(new FocusListener() { public void focusGained(FocusEvent e) { if (_lpexView != null && !isFileReadonly()) { _lpexView.doCommand("set readonly off"); _lpexView.doCommand("screenShow view"); _lpexView.window().textWindow().removeFocusListener(this); } } public void focusLost(FocusEvent e) {} }); } } // start up in originating view's read-only state else { if (fromView != null && fromView.queryOn("readonly")) { _lpexView.doCommand("set readonly on"); } } // listen to a few Shell events _shell.addListener(SWT.Close, new Listener() { public void handleEvent(Event event) { closeShell(event); }}); _shell.addListener(SWT.Dispose, new Listener() { public void handleEvent(Event event) { disposeShell(event); }}); _shell.addListener(SWT.Resize, new Listener() { public void handleEvent(Event event) { resizeShell(event); }}); // start the new view in the same state as the originating one if (fromView != null) { _lpexView.doCommand("set includedClasses " + fromView.query("includedClasses")); _lpexView.doCommand("set excludedClasses " + fromView.query("excludedClasses")); String element = fromView.query("element"); if (element != null) // there are visible elements { _lpexView.doCommand("locate element " + element); _lpexView.doCommand("set cursorRow " + fromView.query("cursorRow")); _lpexView.doCommand("set position " + fromView.query("position")); } } // set Shell's position & size, open it if (bounds == null) { int width = _lpexView.queryInt("userParameter.SwtLpex.width"); int height = _lpexView.queryInt("userParameter.SwtLpex.height"); bounds = new Rectangle(10, 10, (width <= 0)? 674 : width, (height <= 0)? 683 : height); } _shell.setBounds(bounds); _shell.open(); setWindowTitle(); // display view, set input focus in the edit area //_lpexView.doDefaultCommand("screenShow"); //_lpexWindow.textWindow().setFocus(); // run the Shell window: read & dispatch events from the OS queue until it's disposed Display display = _shell.getDisplay(); while (!_shell.isDisposed()) { // no events for us in the queue, sleep to give other applications a chance to run if (!display.readAndDispatch()) { display.sleep(); } } _shell = null; } // Returns document name / locale-sensitive "Untitled Document n". private String documentName() { String name = _lpexView.query("name"); return (name != null)? name : LpexResources.message(LpexMessageConstants.MSG_UNTITLED_DOCUMENT, _lpexView.query("documentId")); } // Returns whether this view's document has an underlying file. private boolean hasUnderlyingFile() { boolean hasFile = false; String name = _lpexView.query("name"); if (name != null && name.length() > 0) { try { hasFile = (new File(name)).exists(); } catch (SecurityException e) // read access denied to file { hasFile = true; } catch (Exception e) {} } return hasFile; } // Returns the read-only state of the underlying file. private boolean isFileReadonly() { boolean readonly = false; String name = _lpexView.query("name"); if (name != null && name.length() > 0) { try { File file = new File(name); readonly = file.exists() && !file.canWrite(); } catch (SecurityException e) // not allowed to read/write file { readonly = true; } catch (Exception e) {} } return readonly; } /*======================*/ /* LPEX notifications */ /*======================*/ // Called after a "set name" command was run. private void renamed(LpexView lpexView) { String name = lpexView.query("name"); if (name != null) { try { String canonicalPath = (new File(name)).getCanonicalPath(); if (canonicalPath != null && !canonicalPath.equals(name)) { lpexView.doDefaultCommand("set name " + canonicalPath); return; } } catch(Exception e) {} } setWindowTitle(); } // Called whenever the "updateProfile" command has been processed. // Defines editor commands, actions & their associated keys. private void updateProfile(LpexView lpexView) { // asked to keep this view as read only if (_browse && !lpexView.queryOn("readonly")) { lpexView.doCommand("set readonly on"); } // "Lpex" command - open an Lpex window lpexView.defineCommand("Lpex", new LpexCommand() { public boolean doCommand(LpexView view, String parameters) { return newWindow(parameters, false, false); } }); // "e" as synonym for the "Lpex" command lpexView.defineCommand("e", new LpexCommand() { public boolean doCommand(LpexView view, String parameters) { return view.doCommand("Lpex " + parameters); } }); // "quit" command - unconditionally quit this Lpex window // (also needed by base profile vi's ":q" commands) lpexView.defineCommand("quit", new LpexCommand() { public boolean doCommand(LpexView view, String parameters) { quit(); return true; } }); // "exit" command - safely close all Lpex views of the document lpexView.defineCommand("exit", new LpexCommand() { public boolean doCommand(LpexView view, String parameters) { _lpexViewNode.disposeAllLpex(); return true; } }); // "openNewView" command - open a new Lpex window on the same document lpexView.defineCommand("openNewView", new LpexCommand() { public boolean doCommand(LpexView view, String parameters) { if (view != null) view.doAction(view.actionId("openNewView")); return true; } }); // "close" action (F3) - optionally save & close this Lpex window lpexView.defineAction("close", new LpexAction() { public void doAction(LpexView view) { close(); } public boolean available(LpexView view) { return true; } }); assignKey("f3", "close"); // ol' style close with F3 // "openNewView" action (Ctrl+O) - open a new Lpex window on the same document lpexView.defineAction("openNewView", new LpexAction() { public void doAction(LpexView view) { newWindow(Lpex.this, Lpex.this._browse); } public boolean available(LpexView view) { return true; } }); assignKey("c-o", "openNewView"); // "nextView" action (Ctrl+Alt+->) - go to the next view of this document lpexView.defineAction("nextView", new LpexAction() { public void doAction(LpexView view) { LpexView v = Lpex.this._lpexViewNode.nextLpexView(); if (v != null && v.window() != null) v.window().setFocus(); } public boolean available(LpexView view) { return true; } }); assignKey("a-s-right", "nextView"); // "prevView" action (Ctrl+Alt+<-) - go to the previous view of this document lpexView.defineAction("prevView", new LpexAction() { public void doAction(LpexView view) { LpexView v = Lpex.this._lpexViewNode.prevLpexView(); if (v != null && v.window() != null) v.window().setFocus(); } public boolean available(LpexView view) { return true; } }); assignKey("a-s-left", "prevView"); // "view log | profile" command - view the LPEX editor log / defaults profile lpexView.defineCommand("view", new LpexCommand() { public boolean doCommand(LpexView view, String parameters) { if ("log".equals(parameters)) { return newWindow('\"' + LpexView.globalQuery("editorLog") + '\"', true, true); } if ("profile".equals(parameters)) { LpexView.doGlobalCommand("profile flush"); return newWindow('\"' + LpexView.globalQuery("defaultProfile") + '\"', true, true); } return (view != null)? view.doCommand("set messageText Syntax: view { log | profile }") : true; }}); // "test" command - placeholder for testing new commands lpexView.defineCommand("test", new LpexCommand() { public boolean doCommand(LpexView view, String parameters) { return (view != null)? view.doCommand("set messageText test: " + parameters) : true; } }); // "test" action - placeholder for testing new actions lpexView.defineAction("test", new LpexAction() { public void doAction(LpexView view) { view.doCommand("set messageText test action"); } public boolean available(LpexView view) { return true; } }); // if there is a master view in the chain, delegate to it potential // application-sensitive actions and commands, such as "save" Delegate delegate = _lpexViewNode.findDelegate(); if (delegate != null) { delegate.delegate(lpexView); } } // Assigns an action to the given key (e.g., "c-o") if free in any context. private void assignKey(String key, String actionName) { if (!_lpexView.keyAssigned(key + ".t")) // text area _lpexView.doCommand("set keyAction." + key + ".t " + actionName); if (!_lpexView.keyAssigned(key + ".p")) // prefix area _lpexView.doCommand("set keyAction." + key + ".p " + actionName); if (!_lpexView.keyAssigned(key + ".c")) // command line _lpexView.doCommand("set keyAction." + key + ".c " + actionName); } // Unconditionally quits this Lpex window. private void quit() { _lpexView.dispose(); _shell.dispose(); } // Safely closes this Lpex window. private void close() { _shell.close(); } // Sets the title of an Lpex window. void setWindowTitle() { String title = "SWT Lpex - " + documentName(); if (_lpexView.queryInt("documentViews") > 1) { title += ": " + _lpexView.query("viewId"); } _shell.setText(title); } // Creates an Lpex window for a new file. If the file is already opened in // the editor, just goes to a view handling it, optionally refreshing it first. private boolean newWindow(String parameters, final boolean browse, boolean refresh) { final String[] parms = LpexStringTokenizer.split(parameters); // extract file name, see if it is already opened in the editor String filename = (new LpexParameters(parms)).filename(); if (filename != null) { LpexView lpexView = _lpexView.lpexView(filename); if (lpexView != null && lpexView.window() != null) { if (refresh) { lpexView.doAction(lpexView.actionId("reload")); } if (browse) { lpexView.doCommand("set readonly on"); // re-set "readonly" after reload } lpexView.window().setFocus(); lpexView.doAction(lpexView.actionId("textWindow")); return true; } } // if new, open a new document for the file _shell.getDisplay().asyncExec(new Runnable() { // à la SwingUtilities#invokeLater() public void run() { new Lpex(parms, newWindowBounds(), browse, null); }}); return true; } // Creates an additional Lpex window for an already-open document. private boolean newWindow(final Lpex lpex, final boolean browse) { _shell.getDisplay().asyncExec(new Runnable() { public void run() { try { new Lpex(lpex._lpexViewNode, newWindowBounds(), browse); } catch(LpexView.ViewInstantiationException e) {} }}); return true; } // Returns acceptable bounds for a new window. private Rectangle newWindowBounds() { if (_shell.getMaximized()) { return new Rectangle(5, 5, _lpexView.queryInt("userParameter.SwtLpex.width"), _lpexView.queryInt("userParameter.SwtLpex.height")); } // new window a bit to the right, down by less than title height unless too low Rectangle newBounds = _shell.getBounds(); Rectangle shellTrim = _shell.computeTrim(0, 0, 0, 0); newBounds.x -= shellTrim.x - 3; newBounds.y -= shellTrim.y + 2; Rectangle displayBounds = _shell.getDisplay().getBounds(); if (newBounds.y + newBounds.height > displayBounds.height) { newBounds.y = 5; } return newBounds; } /*=============================*/ /* Shell event notifications */ /*=============================*/ // Shell is being disposed. private void disposeShell(Event event) { // remove our listeners to the external program's Shell if (_papaShellListener != null) { _papaShell.removeListener(SWT.Close, _papaShellListener); _papaShell.removeListener(SWT.Dispose, _papaShellListener); _papaShellListener = null; } // dispose of our view & its resources (document parser, etc.) if (_lpexView != null) { _lpexView.dispose(); } } // Shell size was set / Shell was resized. private void resizeShell(Event event) { Rectangle rect = _shell.getClientArea(); _lpexWindow.setBounds(0, 0, rect.width, rect.height); rect = _shell.getBounds(); if (!_shell.getMaximized()) { _lpexView.doCommand("set userParameter.SwtLpex.width " + rect.width); _lpexView.doCommand("set userParameter.SwtLpex.height " + rect.height); } } // Shell close request. private void closeShell(Event event) { event.doit = closeWindow(); } // Checks whether this Lpex window can be safely closed (saved / user-abandoned). private boolean closeWindow() { boolean close = true; if (_lpexView != null && _lpexView.queryInt("documentViews") == 1 && // last view? (_lpexView.queryInt("changes") != 0 || _lpexView.queryOn("dirty"))) { MessageBox box = new MessageBox(_shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL); box.setText(_shell.getText()); box.setMessage(LpexResources.message(LpexMessageConstants.MSG_FILE_SAVE_CHANGES, documentName())); int rc = box.open(); if (rc == SWT.YES) { _lpexView.doCommand("save"); if (_lpexView.query("status") != null) // save failed / was cancelled { close = false; } } else if (rc == SWT.CANCEL) { close = false; } } return close; } /** * This interface allows an external program to have actions and commands of * secondary {@link com.ibm.lpex.samples.Lpex Lpex} views that it creates, * delegated to its own master view of the document. */ public interface Delegate { /** * Redefine actions and commands of the given Lpex view. */ public void delegate(LpexView lpexView); } // keep a list of all Lpex view chains private static ArrayList<LpexViewNode> _viewChains = new ArrayList<LpexViewNode>(); /** * LpexView node for the management of multiple views of one LPEX document. */ static final class LpexViewNode { private LpexViewNode _prev; private LpexViewNode _next; private LpexView _lpexView; private Lpex _lpex; // null = a non-Lpex (external program) master view node private Delegate _delegate; // action/command delegation to the master LpexView // Constructs a top node for an Lpex view. LpexViewNode(Lpex lpex) { _lpex = lpex; _lpexView = lpex._lpexView; _viewChains.add(this); } // Chains a new view node after another node of the same document. LpexViewNode(Lpex newLpex, LpexViewNode oldNode) { _lpex = newLpex; _lpexView = newLpex._lpexView; _prev = oldNode; _next = oldNode._next; if (oldNode._next != null) { oldNode._next._prev = this; } oldNode._next = this; } // Constructs a top node for an external program's master view. private LpexViewNode(LpexView lpexView, Delegate delegate) { _lpexView = lpexView; _delegate = delegate; _viewChains.add(this); _lpexView.addLpexViewListener(new LpexViewAdapter() { // called when the master LpexView is disposed public void disposed(LpexView view) { // clear chain, we may have delegated actions to the now-disposed view disposeAllLpex(); remove(); }}); } // Finds an existing, or constructs a new, top node // for an external program's master view. static LpexViewNode getLpexViewNode(LpexView lpexView, Delegate delegate) { for (int i = 0; i < _viewChains.size(); i++) { LpexViewNode lpexViewNode = _viewChains.get(i); if (lpexViewNode._lpexView == lpexView) { return lpexViewNode; } } // not found, start new view chain return new LpexViewNode(lpexView, delegate); } // Safely disposes of all document's Lpex views. void disposeAllLpex() { // dispose all around the top master view, if any LpexViewNode node = this; while (node != null && node._lpex != null && node._prev != null) { node = node._prev; } while (node._next != null) { node._next._lpex.quit(); } while (node._prev != null) { node._prev._lpex.quit(); } // if not an external-program view, close top view as well if (node._lpex != null) { node._lpex.close(); } } // Removes node. void remove() { // unchain if (_prev != null) { _prev._next = _next; } if (_next != null) { _next._prev = _prev; } // cut self off from Lpex if (_lpex != null) { _lpex._lpexView = null; _lpex._lpexViewNode = null; } // if a top node, remove from list of view chains int i = _viewChains.indexOf(this); if (i >= 0) { _viewChains.remove(i); } } // Returns this node's view. LpexView lpexView() { return _lpexView; } // Finds document chain's next view. LpexView nextLpexView() { LpexViewNode node = _next; if (node == null) { for (node = _prev; node != null && node._prev != null; node = node._prev) {} } return (node != null)? node._lpexView : null; } // Finds document chain's previous view. LpexView prevLpexView() { LpexViewNode node = _prev; if (node == null) { for (node = _next; node != null && node._next != null; node = node._next) {} } return (node != null)? node._lpexView : null; } // Finds the master view's delegate in this Lpex view's chain. Delegate findDelegate() { for (LpexViewNode node = _prev; node != null; node = node._prev) { if (node._lpex == null) { return node._delegate; } } return null; } } } /** * This class extracts the Lpex parameters. */ final class LpexParameters { private String _filename; // file name private String _encoding = ""; // defaults to autodetect file's encoding private String _documentType; // overriding document type (e.g., file-name extension) LpexParameters(String[] parms) { for (int i = 0; i < parms.length; i++) { // parameter? if (i < parms.length-1 && (parms[i].startsWith("-") || parms[i].startsWith("/"))) { String parm = parms[i].substring(1); if ("enc".equals(parm) || "encoding".equals(parm)) { _encoding = parms[++i]; continue; } else if ("dt".equals(parm) || "extension".equals(parm)) { _documentType = parms[++i]; continue; } } // file name? if (i == 0) { _filename = LpexStringTokenizer.trimQuotes(parms[i]); } // unrecognized parameter else { break; } } } String filename() { return _filename; } String encoding() { return _encoding; } String documentType() { return _documentType; } }