Package org.jmol.viewer

Source Code of org.jmol.viewer.FileManager

/* $RCSfile$
* $Author: hansonr $
* $Date: 2011-02-19 20:07:38 +0100 (sam., 19 févr. 2011) $
* $Revision: 15202 $
*
* Copyright (C) 2003-2005  Miguel, Jmol Development Team
*
* Contact: jmol-developers@lists.sf.net
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library 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
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jmol.viewer;

import org.jmol.util.ArrayUtil;
import org.jmol.util.BinaryDocument;
import org.jmol.util.CompoundDocument;
import org.jmol.util.Logger;
import org.jmol.util.Parser;
import org.jmol.util.TextFormat;
import org.jmol.util.ZipUtil;


import org.jmol.api.JmolFileReaderInterface;
import org.jmol.api.JmolViewer;

import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.net.MalformedURLException;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.Reader;
import java.text.DateFormat;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.ArrayList;

public class FileManager {

  protected Viewer viewer;

  FileManager(Viewer viewer) {
    this.viewer = viewer;
    clear();
  }

  void clear() {
    fullPathName = fileName = nameAsGiven = viewer.getZapName();
  }

  private void setLoadState(Hashtable htParams) {
    if (viewer.getPreserveState()) {
      htParams.put("loadState", viewer.getLoadState());
    }
  }

  String nameAsGiven = "zapped";
  String fullPathName;
  String fileName;

  void setFileInfo(String[] fileInfo) {
    // used by ScriptEvaluator dataFrame and load methods to temporarily save the state here
    fullPathName = fileInfo[0];
    fileName = fileInfo[1];
    nameAsGiven = fileInfo[2];
  }

  String[] getFileInfo() {
    // used by ScriptEvaluator dataFrame method
    return new String[] { fullPathName, fileName, nameAsGiven };
  }

  String getFullPathName() {
    return fullPathName != null ? fullPathName : nameAsGiven;
  }

  String getFileName() {
    return fileName != null ? fileName : nameAsGiven;
  }

  // for applet proxy
  private URL appletDocumentBase = null;
  private URL appletCodeBase = null; //unused currently
  private String appletProxy;

  String getAppletDocumentBase() {
    return (appletDocumentBase == null ? "" : appletDocumentBase.toString());
  }

  void setAppletContext(URL documentBase, URL codeBase, String jmolAppletProxy) {
    appletDocumentBase = documentBase;
    appletCodeBase = codeBase;
    appletProxy = jmolAppletProxy;
    Logger.info("appletDocumentBase=" + appletDocumentBase
        + "\nappletCodeBase=" + appletCodeBase);
  }

  void setAppletProxy(String appletProxy) {
    this.appletProxy = (appletProxy == null || appletProxy.length() == 0 ? null
        : appletProxy);
  }

  String getState(StringBuffer sfunc) {
    StringBuffer commands = new StringBuffer();
    if (sfunc != null) {
      sfunc.append("  _setFileState;\n");
      commands.append("function _setFileState() {\n\n");
    }
    viewer.appendLoadStates(commands);
    if (commands.indexOf("append") < 0 && viewer.getModelSetFileName().equals("zapped"))
      commands.append("  zap;\n");
    if (sfunc != null)
      commands.append("\n}\n\n");
    return commands.toString();
  }

  String getFileTypeName(String fileName) {
    int pt = fileName.indexOf("::");
    if (pt >= 0)
      return fileName.substring(0, pt);
    if (fileName.startsWith("="))
      return "pdb";
    String type = null;
    try {
      Object br = getUnzippedBufferedReaderOrErrorMessageFromName(fileName, true,
          false, true, true);
      if (br instanceof BufferedReader) {
        type = viewer.getModelAdapter().getFileTypeName((BufferedReader) br);
        ((BufferedReader) br).close();
      } else if (br instanceof InputStream) {
        String zipDirectory = getZipDirectoryAsString(fileName);
        type = (zipDirectory.indexOf("JmolManifest") >= 0 ? "Jmol"
            : viewer.getModelAdapter().getFileTypeName(
              getBufferedReaderForString(zipDirectory)));
        ((InputStream) br).close();
      } else if (br instanceof String[]) {
        type = ((String[]) br)[0];
      }
    } catch (IOException e) {
      // TODO
    }
    return type;
  }

  private static BufferedReader getBufferedReaderForString(String string) {
    return new BufferedReader(new StringReader(string));
  }

  private String getZipDirectoryAsString(String fileName) {
    return ZipUtil
        .getZipDirectoryAsStringAndClose((InputStream) getInputStreamOrErrorMessageFromName(
            fileName, false, false));
  }

  /////////////// createAtomSetCollectionFromXXX methods /////////////////
 
  // where XXX = File, Files, String, Strings, ArrayData, DOM, Reader
 
  /*
   * note -- createAtomSetCollectionFromXXX methods
   * were "openXXX" before refactoring 11/29/2008 -- BH
   *
   * The problem was that while they did open the file, they
   * (mostly) also closed them, and this was confusing.
   *
   * The term "clientFile" was replaced by "atomSetCollection"
   * here because that's what it is --- an AtomSetCollection,
   * not a file. The file is closed at this point. What is
   * returned is the atomSetCollection object.
   *
   * One could say this is just semantics, but there were
   * subtle bugs here, where readers were not always being
   * closed explicitly. In the process of identifying Out of
   * Memory Errors, I felt it was necessary to clarify all this.
   *
   * Apologies to those who feel the original clientFile notation
   * was more generalizable or understandable.
   *
   */
  Object createAtomSetCollectionFromFile(String name, Hashtable htParams,
                                    StringBuffer loadScript, boolean isAppend) {
    if (htParams.get("atomDataOnly") == null)
      setLoadState(htParams);
    if (name.indexOf('=') == 0 || name.indexOf('$') == 0)
      name = (String) viewer.setLoadFormat(name, name.charAt(0), true);
    int pt = name.indexOf("::");
    String nameAsGiven = (pt >= 0 ? name.substring(pt + 2) : name);
    String fileType = (pt >= 0 ? name.substring(0, pt) : null);
    Logger.info("\nFileManager.getAtomSetCollectionFromFile(" + nameAsGiven
        + ")" + (name.equals(nameAsGiven) ? "" : " //" + name));
    String[] names = classifyName(nameAsGiven, true);
    if (names.length == 1)
      return names[0];
    String fullPathName = names[0];
    String fileName = names[1];
    htParams.put("fullPathName", (fileType == null ? "" : fileType + "::")
        + fullPathName.replace('\\', '/'));
    if (viewer.getMessageStyleChime() && viewer.getDebugScript())
      viewer.scriptStatus("Requesting " + fullPathName);
    FileReader fileReader = new FileReader(fileName, fullPathName, nameAsGiven,
        fileType, null, htParams, isAppend);
    fileReader.run();
    return fileReader.atomSetCollection;
  }

  Object createAtomSetCollectionFromFiles(String[] fileNames,
                                     Hashtable htParams,
                                     boolean isAppend) {
    setLoadState(htParams);
    String[] fullPathNames = new String[fileNames.length];
    String[] namesAsGiven = new String[fileNames.length];
    String[] fileTypes = new String[fileNames.length];
    for (int i = 0; i < fileNames.length; i++) {
      int pt = fileNames[i].indexOf("::");
      String nameAsGiven = (pt >= 0 ? fileNames[i].substring(pt + 2) : fileNames[i]);
      String fileType = (pt >= 0 ? fileNames[i].substring(0, pt) : null);
      String[] names = classifyName(nameAsGiven, true);
      if (names.length == 1)
        return names[0];
      fullPathNames[i] = names[0];
      fileNames[i] = names[0].replace('\\', '/');
      fileTypes[i] = fileType;
      namesAsGiven[i] = nameAsGiven;
    }
    htParams.put("fullPathNames", fullPathNames);
    htParams.put("fileTypes", fileTypes);
    FilesReader filesReader = new FilesReader(fullPathNames, namesAsGiven,
        fileTypes, null, htParams, isAppend);
    filesReader.run();
    return filesReader.atomSetCollection;
  }

  Object createAtomSetCollectionFromString(String strModel, StringBuffer loadScript, Hashtable htParams,
                                           boolean isAppend,
                                           boolean isLoadVariable) {
    if (!isLoadVariable)
      DataManager.getInlineData(loadScript, strModel, isAppend, viewer.getDefaultLoadFilter());
    setLoadState(htParams);
    Logger.info("FileManager.getAtomSetCollectionFromString()");
    FileReader fileReader = new FileReader("string", "string", "string", null,
        getBufferedReaderForString(strModel), htParams, isAppend);
    fileReader.run();
    if (!isAppend && !(fileReader.atomSetCollection instanceof String)) {
      viewer.zap(false, true, false);
      fullPathName = fileName = (strModel == JmolConstants.MODELKIT_ZAP_STRING ? JmolConstants.MODELKIT_ZAP_TITLE : "string");
    }
    return fileReader.atomSetCollection;
  }

  Object createAtomSeCollectionFromStrings(String[] arrayModels,
                                           StringBuffer loadScript,
                                           Hashtable htParams, boolean isAppend) {
    if (!htParams.containsKey("isData")) {
      String oldSep = "\"" + viewer.getDataSeparator() + "\"";
      String tag = "\"" + (isAppend ? "append" : "model") + " inline\"";
      StringBuffer sb = new StringBuffer(
          "set dataSeparator \"~~~next file~~~\";\ndata ");
      sb.append(tag);
      for (int i = 0; i < arrayModels.length; i++) {
        if (i > 0)
          sb.append("~~~next file~~~");
        sb.append(arrayModels[i]);
      }
      sb.append("end ").append(tag).append(";set dataSeparator ")
          .append(oldSep);
      loadScript.append(sb);
    }
    setLoadState(htParams);
    Logger.info("FileManager.getAtomSetCollectionFromStrings(string[])");
    String[] fullPathNames = new String[arrayModels.length];
    StringDataReader[] readers = new StringDataReader[arrayModels.length];
    for (int i = 0; i < arrayModels.length; i++) {
      fullPathNames[i] = "string[" + i + "]";
      readers[i] = new StringDataReader(arrayModels[i]);
    }
    FilesReader filesReader = new FilesReader(fullPathNames, fullPathNames,
        null, readers, htParams, isAppend);
    filesReader.run();
    return filesReader.atomSetCollection;
  }

  Object createAtomSeCollectionFromArrayData(List arrayData,
                                               Hashtable htParams,
                                               boolean isAppend) {
    // NO STATE SCRIPT -- HERE WE ARE TRYING TO CONSERVE SPACE
    Logger.info("FileManager.getAtomSetCollectionFromArrayData(List)");
    int nModels = arrayData.size();
    String[] fullPathNames = new String[nModels];
    DataReader[] readers = new DataReader[nModels];
    for (int i = 0; i < nModels; i++) {
      fullPathNames[i] = "String[" + i + "]";
      Object data = arrayData.get(i);
      if (data instanceof String)
        readers[i] = new StringDataReader((String) arrayData.get(i));
      else if (data instanceof String[])
        readers[i] = new ArrayDataReader((String[]) arrayData.get(i));
      else if (data instanceof List)
        readers[i] = new VectorDataReader((List) arrayData.get(i));
    }
    FilesReader filesReader = new FilesReader(fullPathNames, fullPathNames,
        null, readers, null, isAppend);
    filesReader.run();
    return filesReader.atomSetCollection;
  }

  Object createAtomSetCollectionFromDOM(Object DOMNode, Hashtable htParams) {
    DOMReader aDOMReader = new DOMReader(DOMNode, htParams);
    aDOMReader.run();
    return aDOMReader.atomSetCollection;
  }

  /**
   * not used in Jmol project -- will close reader
   *
   * @param fullPathName
   * @param name
   * @param reader
   * @param htParams
   * @return fileData
   */
  Object createAtomSetCollectionFromReader(String fullPathName, String name,
                                      Reader reader, Hashtable htParams) {
    FileReader fileReader = new FileReader(name, fullPathName, name,
        null, new BufferedReader(reader), htParams, false);
    fileReader.run();
    return fileReader.atomSetCollection;
  }

  /////////////// generally useful file I/O methods /////////////////
 
  // mostly internal to FileManager and its enclosed classes
 
  BufferedInputStream getBufferedInputStream(String fullPathName) {
    Object ret = getBufferedReaderOrErrorMessageFromName(fullPathName,
        new String[2], true, true);
    return (ret instanceof BufferedInputStream ? (BufferedInputStream) ret
        : null);
  }

  Object getInputStreamOrErrorMessageFromName(String name, boolean showMsg,
                                              boolean checkOnly) {
    return getInputStreamOrPost(name, showMsg, null, checkOnly, appletDocumentBase, appletProxy);
  }
  private static Object getInputStreamOrPost(String name, boolean showMsg,
                                              byte[] bytes, boolean checkOnly,
                                              URL appletDocumentBase,
                                              String appletProxy) {
    String errorMessage = null;
    int iurl;
    for (iurl = urlPrefixes.length; --iurl >= 0;)
      if (name.startsWith(urlPrefixes[iurl]))
        break;
    boolean isURL = (iurl >= 0);
    String post = null;
    if (isURL && (iurl = name.indexOf("?POST?")) >= 0) {
      post = name.substring(iurl + 6);
      name = name.substring(0, iurl);     
    }
    boolean isApplet = (appletDocumentBase != null);
    InputStream in = null;
    // int length;
    try {
      if (isApplet || isURL) {
        if (isApplet && isURL && appletProxy != null)
          name = appletProxy + "?url=" + URLEncoder.encode(name, "utf-8");
        URL url = (isApplet ? new URL(appletDocumentBase, name) : new URL(name));
        name = url.toString();
        if (showMsg && !checkOnly)
          Logger.info("FileManager opening " + url.toString());
        URLConnection conn = url.openConnection();
        if (bytes != null && !checkOnly) {
          conn.setRequestProperty("Content-Type", "application/octet-stream");
          conn.setDoOutput(true);
          conn.getOutputStream().write(bytes);
        } else if (post != null && !checkOnly) {
          conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
          conn.setDoOutput(true);
          OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
          wr.write(post);
          wr.flush();          
        }         
        in = conn.getInputStream();
      } else {
        if (showMsg)
          Logger.info("FileManager opening " + name);
        File file = new File(name);
        // length = (int) file.length();
        in = new FileInputStream(file);
      }
      if (checkOnly) {
        in.close();
        in = null;
      }
      return in;
    } catch (Exception e) {
      try {
        if (in != null)
          in.close();
      } catch (IOException e1) {
      }
      errorMessage = "" + e;
    }
    return errorMessage;
  }

  /**
   * just check for a file as being readable. Do not go into a zip file
   *
   * @param filename
   * @return String[2] where [0] is fullpathname and [1] is error message or null
   */
  String[] getFullPathNameOrError(String filename) {
    String[] names = classifyName(filename, true);
    if (names == null || names[0] == null || names.length < 2)
      return new String[] { null, "cannot read file name: " + filename };
    String name = names[0];
    String fullPath = names[0].replace('\\', '/');
    if (name.indexOf("|") >= 0)
      name = TextFormat.split(name, "|")[0];
    Object errMsg = getInputStreamOrErrorMessageFromName(name, false, true);
    return new String[] { fullPath,
        (errMsg instanceof String ? (String) errMsg : null) };
  }

  Object getBufferedReaderOrErrorMessageFromName(String name,
                                                 String[] fullPathNameReturn,
                                                 boolean isBinary, boolean doSpecialLoad) {
    String[] names = classifyName(name, true);
    if (names == null)
      return "cannot read file name: " + name;
    if (fullPathNameReturn != null)
      fullPathNameReturn[0] = names[0].replace('\\', '/');
    return getUnzippedBufferedReaderOrErrorMessageFromName(names[0], false,
        isBinary, false, doSpecialLoad);
  }

  Object getUnzippedBufferedReaderOrErrorMessageFromName(
                                                         String name,
                                                         boolean allowZipStream,
                                                         boolean asInputStream,
                                                         boolean isTypeCheckOnly,
                                                         boolean doSpecialLoad) {
    String[] subFileList = null;
    String[] info = (doSpecialLoad ? viewer.getModelAdapter().specialLoad(name,
        "filesNeeded?") : null);
    if (info != null) {
      if (isTypeCheckOnly)
        return info;
      if (info[2] != null) {
        String header = info[1];
        Hashtable fileData = new Hashtable();
        if (info.length == 3) {
          // we need information from the output file, info[2]
          String name0 = getObjectAsSections(info[2], header, fileData);
          fileData.put("OUTPUT", name0);
          info = viewer.getModelAdapter().specialLoad(name,
              (String) fileData.get(name0));
          if (info.length == 3) {
            // might have a second option
            name0 = getObjectAsSections(info[2], header, fileData);
            fileData.put("OUTPUT", name0);
            info = viewer.getModelAdapter().specialLoad(info[1],
                (String) fileData.get(name0));
          }
        }
        // load each file individually, but return files IN ORDER
        StringBuffer sb = new StringBuffer();
        sb.append(fileData.get(fileData.get("OUTPUT")));
        for (int i = 2; i < info.length; i++) {
          name = info[i];
          name = getObjectAsSections(name, header, fileData);
          Logger.info("reading " + name);
          String s = (String) fileData.get(name);
          sb.append(s);
        }
        return getBufferedReaderForString(sb.toString());
      }
      // continuing...
      // here, for example, for an SPT file load that is not just a type check
      // (type check is only for application file opening and drag-drop to
      // determine if
      // script or load command should be used)
    }
    if (name.indexOf("|") >= 0)
      name = (subFileList = TextFormat.split(name, "|"))[0];
    Object t = getInputStreamOrErrorMessageFromName(name, true, false);
    if (t instanceof String)
      return t;
    try {
      BufferedInputStream bis = new BufferedInputStream((InputStream) t, 8192);
      if (CompoundDocument.isCompoundDocument(bis)) {
        CompoundDocument doc = new CompoundDocument(bis);
        return getBufferedReaderForString(doc.getAllData("Molecule", "Input").toString());
      } else if (ZipUtil.isGzip(bis)) {
        do {
          bis = new BufferedInputStream(new GZIPInputStream(bis));
        } while (ZipUtil.isGzip(bis));
      } else if (ZipUtil.isZipFile(bis)) {
        if (allowZipStream)
          return new ZipInputStream(bis);
        if (asInputStream)
          return (BufferedInputStream) ZipUtil.getZipFileContents(bis, subFileList, 1,
              true);
        // danger -- converting bytes to String here.
        // we lose 128-156 or so.
        String s = (String) ZipUtil.getZipFileContents(bis, subFileList, 1,
            false);
        bis.close();
        return getBufferedReaderForString(s);
      }
      if (asInputStream)
        return bis;
      return new BufferedReader(new InputStreamReader(bis));
    } catch (Exception ioe) {
      return ioe.getMessage();
    }
  }

  String[] getZipDirectory(String fileName, boolean addManifest) {
    return ZipUtil.getZipDirectoryAndClose(
        (InputStream) getInputStreamOrErrorMessageFromName(fileName, false, false),
        addManifest);
  }

  /**
   * delivers file contents and directory listing for a ZIP/JAR file into sb
   *
   *
   * @param name
   * @param header
   * @param fileData
   * @return name of entry
   */
  private String getObjectAsSections(String name, String header,
                                     Hashtable fileData) {
    if (name == null)
      return null;
    String[] subFileList = null;
    boolean asBinaryString = false;
    String name0 = name.replace('\\', '/');
    if (name.indexOf(":asBinaryString") >= 0) {
      asBinaryString = true;
      name = name.substring(0, name.indexOf(":asBinaryString"));
    }
    StringBuffer sb = null;
    if (fileData.containsKey(name0))
      return name0;
    if (name.indexOf("#JMOL_MODEL ") >= 0) {
      fileData.put(name0, name0 + "\n");
      return name0;
    }
    if (name.indexOf("|") >= 0) {
      name = (subFileList = TextFormat.split(name, "|"))[0];
    }
    BufferedInputStream bis = null;
    try {
      Object t = getInputStreamOrErrorMessageFromName(name, false, false);
      if (t instanceof String) {
        fileData.put(name0, (String) t + "\n");
        return name0;
      }
      bis = new BufferedInputStream((InputStream) t, 8192);
      if (CompoundDocument.isCompoundDocument(bis)) {
        CompoundDocument doc = new CompoundDocument(bis);
        doc.getAllData(name.replace('\\', '/'), "Molecule", fileData);
      } else if (ZipUtil.isZipFile(bis)) {
        ZipUtil.getAllData(bis, subFileList, name.replace('\\', '/'), "Molecule", fileData);
      } else if (asBinaryString) {
        // used for Spartan binary file reading
        BinaryDocument bd = new BinaryDocument();
        bd.setStream(bis, false);
        sb = new StringBuffer();
        //note -- these headers must match those in ZipUtil.getAllData and CompoundDocument.getAllData
        if (header != null)
          sb.append("BEGIN Directory Entry " + name0 + "\n");
        try {
          while (true)
            sb.append(Integer.toHexString(((int) bd.readByte()) & 0xFF))
                .append(' ');
        } catch (Exception e1) {
          sb.append('\n');
        }
        if (header != null)
          sb.append("\nEND Directory Entry " + name0 + "\n");
        fileData.put(name0, sb.toString());
      } else {
        BufferedReader br = new BufferedReader(new InputStreamReader(
            ZipUtil.isGzip(bis) ? new GZIPInputStream(bis) : (InputStream) bis));
        String line;
        sb = new StringBuffer();
        if (header != null)
          sb.append("BEGIN Directory Entry " + name0 + "\n");
        while ((line = br.readLine()) != null) {
          sb.append(line);
          sb.append('\n');
        }
        br.close();
        if (header != null)
          sb.append("\nEND Directory Entry " + name0 + "\n");
        fileData.put(name0, sb.toString());
      }
    } catch (Exception ioe) {
      fileData.put(name0, ioe.getMessage());
    }
    if (bis != null)
      try {
        bis.close();
      } catch (Exception e) {
        //
      }
    if (!fileData.containsKey(name0))
      fileData.put(name0, "FILE NOT FOUND: " + name0 + "\n");
    return name0;
  }

  Object getFileAsBytes(String name, OutputStream os) {
    // ?? used by eval of "WRITE FILE"
    // will be full path name
    if (name == null)
      return null;
    String[] subFileList = null;
    if (name.indexOf("|") >= 0)
      name = (subFileList = TextFormat.split(name, "|"))[0];
    Object t = getInputStreamOrErrorMessageFromName(name, false, false);
    if (t instanceof String)
      return "Error:" + t;
    try {
      BufferedInputStream bis = new BufferedInputStream((InputStream) t, 8192);
      InputStream is = bis;
      if (os != null)
        return getStreamAsBytes(bis, os);
      Object bytes = (ZipUtil.isZipFile(is) && subFileList != null
          && 1 < subFileList.length ? ZipUtil.getZipFileContentsAsBytes(is,
          subFileList, 1) : getStreamAsBytes(bis, null));
      is.close();
      return bytes;
    } catch (Exception ioe) {
      return ioe.getMessage();
    }
  }

  private static Object getStreamAsBytes(BufferedInputStream bis, OutputStream os) throws IOException {
    byte[] buf = new byte[1024];
    byte[] bytes = (os == null ? new byte[4096] : null);
    int len = 0;
    int totalLen = 0;
    while ((len = bis.read(buf)) > 0) {
      totalLen += len;
      if (os == null) {
        if (totalLen >= bytes.length)
          bytes = ArrayUtil.ensureLength(bytes, totalLen * 2);
        System.arraycopy(buf, 0, bytes, totalLen - len, len);
      } else {
        os.write(buf, 0, len);
      }
    }
    if (os == null) {
      buf = new byte[totalLen];
      System.arraycopy(bytes, 0, buf, 0, totalLen);
      return buf;
    }
    return totalLen + " bytes";
  }


  /**
   *
   * @param data
   *          [0] initially path name, but returned as full path name; [1]file
   *          contents (directory listing for a ZIP/JAR file) or error string
   * @param nBytesMax
   * @param doSpecialLoad
   * @return true if successful; false on error
   */

  boolean getFileDataOrErrorAsString(String[] data, int nBytesMax, boolean doSpecialLoad) {
    data[1] = "";
    String name = data[0];
    if (name == null)
      return false;
    Object t = getBufferedReaderOrErrorMessageFromName(name, data, false, doSpecialLoad);
    if (t instanceof String) {
      data[1] = (String) t;
      return false;
    }
    try {
      BufferedReader br = (BufferedReader) t;
      StringBuffer sb = new StringBuffer(8192);
      String line;
      if (nBytesMax == Integer.MAX_VALUE) {
        while ((line = br.readLine()) != null)
          sb.append(line).append('\n');
      } else {
        int n = 0;
        int len;
        while (n < nBytesMax && (line = br.readLine()) != null) {
          if (nBytesMax - n < (len = line.length()) + 1)
            line = line.substring(0, nBytesMax - n - 1);
          sb.append(line).append('\n');
          n += len + 1;
        }
      }
      br.close();
      data[1] = sb.toString();
      return true;
    } catch (Exception ioe) {
      data[1] = ioe.getMessage();
      return false;
    }
  }

  Object getFileAsImage(String name, Hashtable htParams) {
    if (name == null)
      return "";
    String[] names = classifyName(name, true);
    if (names == null)
      return "cannot read file name: " + name;
    Image image = null;
    //try {
    String fullPathName = names[0].replace('\\', '/');
    if (fullPathName.indexOf("|") > 0) {
      Object ret = getFileAsBytes(fullPathName, null);
      if (!(ret instanceof byte[]))
        return "" + ret;
      image = Toolkit.getDefaultToolkit().createImage((byte[]) ret);
    } else if (urlTypeIndex(fullPathName) >= 0) {
      try {
        image = Toolkit.getDefaultToolkit().createImage(new URL(fullPathName));
      } catch (Exception e) {
        return "bad URL: " + fullPathName;
      }
    } else {
      image = Toolkit.getDefaultToolkit().createImage(fullPathName);
    }
    try {
      MediaTracker mediaTracker = new MediaTracker(viewer.getDisplay());
      mediaTracker.addImage(image, 0);
      mediaTracker.waitForID(0);
      /* SUN but here for malformed URL - can't trap
       Uncaught error fetching image:
       java.lang.NullPointerException
       at sun.net.www.ParseUtil.toURI(Unknown Source)
       at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
       at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
       at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
       at sun.awt.image.URLImageSource.getDecoder(Unknown Source)
       at sun.awt.image.InputStreamImageSource.doFetch(Unknown Source)
       at sun.awt.image.ImageFetcher.fetchloop(Unknown Source)
       at sun.awt.image.ImageFetcher.run(Unknown Source)
       */
    } catch (Exception e) {
      return e.getMessage() + " opening " + fullPathName;
    }
    if (image.getWidth(null) < 1)
      return "invalid or missing image " + fullPathName;
    htParams.put("fullPathName", fullPathName);
    return image;
  }

  private final static int URL_LOCAL = 3;
  private final static String[] urlPrefixes = { "http:", "https:", "ftp:",
      "file:" };

  private static int urlTypeIndex(String name) {
    for (int i = 0; i < urlPrefixes.length; ++i) {
      if (name.startsWith(urlPrefixes[i])) {
        return i;
      }
    }
    return -1;
  }

  /**
   *
   * @param name
   * @param isFullLoad
   * @return [0] full path name, [1] file name without path, [2] full URL
   */
  private String[] classifyName(String name, boolean isFullLoad) {
    if (name == null)
      return new String[] { null };
    if (name.startsWith("?")
        && (name = viewer.dialogAsk("load", name.substring(1))) == null) {
      return new String[] { isFullLoad ? "#CANCELED#" : null };
    }
    File file = null;
    URL url = null;
    String[] names = null;
    if (name.indexOf("=") == 0 || name.indexOf('$') == 0)
      name = (String) viewer.setLoadFormat(name, name.charAt(0), false);
    if (name.indexOf(":") < 0 && name.indexOf("/") != 0)
      name = addDirectory(viewer.getDefaultDirectory(), name);
    if (appletDocumentBase != null) {
      // This code is only for the applet
      try {
        if (name.indexOf(":\\") == 1 || name.indexOf(":/") == 1)
          name = "file:/" + name;
//        else if (name.indexOf("/") == 0 && viewer.isSignedApplet())
  //        name = "file:" + name;
        url = new URL(appletDocumentBase, name);
      } catch (MalformedURLException e) {
        return new String[] { isFullLoad ? e.getMessage() : null };
      }
    } else {
      // This code is for the app
      if (urlTypeIndex(name) >= 0) {
        try {
          url = new URL(name);
        } catch (MalformedURLException e) {
          return new String[] { isFullLoad ? e.getMessage() : null };
        }
      } else {
        file = new File(name);
        names = new String[] { file.getAbsolutePath(), file.getName(),
            "file:/" + file.getAbsolutePath().replace('\\', '/') };
      }
    }
    if (url != null) {
      names = new String[3];
      names[0] = names[2] = url.toString();
      names[1] = names[0].substring(names[0].lastIndexOf('/') + 1);
    }
    if (isFullLoad && (file != null || urlTypeIndex(names[0]) == URL_LOCAL)) {
      String path = (file == null ? TextFormat.trim(names[0].substring(5), "/")
          : names[0]);
      int pt = path.length() - names[1].length() - 1;
      if (pt > 0) {
        path = path.substring(0, pt);
        setLocalPath(viewer, path, true);
      }
    }
    return names;
  }

  private static String addDirectory(String defaultDirectory, String name) {
    if (defaultDirectory.length() == 0)
      return name;
    char ch = (name.length() > 0 ? name.charAt(0) : ' ');
    String s = defaultDirectory.toLowerCase();
    if ((s.endsWith(".zip") || s.endsWith(".tar")) && ch != '|' && ch != '/')
      defaultDirectory += "|";
    return defaultDirectory
        + (ch == '/'
            || ch == '/'
            || (ch = defaultDirectory.charAt(defaultDirectory.length() - 1)) == '|'
            || ch == '/' ? "" : "/") + name;
  }

  String getDefaultDirectory(String name) {
    String[] names = classifyName(name, true);
    if (names == null)
      return "";
    name = fixPath(names[0]);
    return (names == null ? "" : name.substring(0, name.lastIndexOf("/")));
  }

  private static String fixPath(String path) {
    path = path.replace('\\', '/');
    path = TextFormat.simpleReplace(path, "/./", "/");
    int pt = path.lastIndexOf("//") + 1;
    if (pt < 1)
      pt = path.indexOf(":/") + 1;
    if (pt < 1)
      pt = path.indexOf("/");
    String protocol = path.substring(0, pt);
    path = path.substring(pt);

    while ((pt = path.lastIndexOf("/../")) >= 0) {
      int pt0 = path.substring(0, pt).lastIndexOf("/");
      if (pt0 < 0)
        return TextFormat.simpleReplace(protocol + path, "/../", "/");
      path = path.substring(0, pt0) + path.substring(pt + 3);
    }
    if (path.length() == 0)
      path = "/";
    return protocol + path;
  }

  public String getFilePath(String name, boolean addUrlPrefix, boolean asShortName) {
    String[] names = classifyName(name, false);
    return (names == null ? "" : asShortName ? names[1]
        : addUrlPrefix ? names[2] : names[0].replace('\\', '/'));
  }

  private final static String[] urlPrefixPairs = { "http:", "http://", "www.",
      "http://www.", "https:", "https://", "ftp:", "ftp://", "file:",
      "file:///" };

  public static String getLocalUrl(File file) {
    // entering a url on a file input box will be accepted,
    // but cause an error later. We can fix that...
    // return null if there is no problem, the real url if there is
    if (file.getName().startsWith("="))
      return file.getName();
    String path = file.getAbsolutePath().replace('\\', '/');
    for (int i = 0; i < urlPrefixPairs.length; i++)
      if (path.indexOf(urlPrefixPairs[i]) == 0)
        return null;
    // looking for /xxx/xxxx/file://...
    for (int i = 0; i < urlPrefixPairs.length; i += 2)
      if (path.indexOf(urlPrefixPairs[i]) > 0)
        return urlPrefixPairs[i + 1]
            + TextFormat.trim(path.substring(path.indexOf(urlPrefixPairs[i])
                + urlPrefixPairs[i].length()), "/");
    return null;
  }

  public static File getLocalDirectory(JmolViewer viewer, boolean forDialog) {
    String localDir = (String) viewer
        .getParameter(forDialog ? "currentLocalPath" : "defaultDirectoryLocal");
    if (localDir.length() == 0 && forDialog)
      localDir = (String) viewer.getParameter("defaultDirectoryLocal");
    if (localDir.length() == 0)
      return (viewer.isApplet() ? null : new File(System.getProperty("user.dir")));
    if (viewer.isApplet() && localDir.indexOf("file:/") == 0)
        localDir = getLocalPathForWritingFile(viewer, localDir);
   
    File f = new File(localDir);
    return f.isDirectory() ? f : f.getParentFile();
  }

  public static void setLocalPath(JmolViewer viewer, String path,
                                  boolean forDialog) {
    while (path.endsWith("/") || path.endsWith("\\"))
      path = path.substring(0, path.length() - 1);
    viewer.setStringProperty("currentLocalPath", path);
    if (!forDialog)
      viewer.setStringProperty("defaultDirectoryLocal", path);
  }

  public static String getLocalPathForWritingFile(JmolViewer viewer, String file) {
    if (file.indexOf("file:/") == 0)
      return file.substring(6);
    if (file.indexOf("/") == 0 || file.indexOf(":") >= 0)
      return file;
    File dir = getLocalDirectory(viewer, false);
    return (dir == null ? file : fixPath(dir.toString() + "/" + file));
  }

  public static String setScriptFileReferences(String script, String localPath,
                                               String remotePath, String scriptPath) {
      if (localPath != null)
        script = setScriptFileReferences(script, localPath, true);
      if (remotePath != null)
        script = setScriptFileReferences(script, remotePath, false);
      script = TextFormat.simpleReplace(script, "\1\"", "\"");
      if (scriptPath != null) {
        while (scriptPath.endsWith("/"))
          scriptPath = scriptPath.substring(0, scriptPath.length() - 1);
        for (int ipt = 0; ipt < scriptFilePrefixes.length; ipt++) {
          String tag = scriptFilePrefixes[ipt];
          script = TextFormat.simpleReplace(script, tag + ".", tag + scriptPath);
        }
      }
      return script;
  }
  /**
   * Sets all local file references in a script file to point to files within
   * dataPath. If a file reference contains dataPath, then the file reference is
   * left with that RELATIVE path. Otherwise, it is changed to a relative file
   * name within that dataPath.
   *
   * Only file references starting with "file://" are changed.
   *
   * @param script
   * @param dataPath
   * @param isLocal
   * @return revised script
   */
  private static String setScriptFileReferences(String script, String dataPath, boolean isLocal) {
    if (dataPath == null)
      return script;
    boolean noPath = (dataPath.length() == 0);
    List fileNames = new ArrayList();
    getFileReferences(script, fileNames);
    List oldFileNames = new ArrayList();
    List newFileNames = new ArrayList();
    int nFiles = fileNames.size();
    for (int iFile = 0; iFile < nFiles; iFile++) {
      String name0 = (String) fileNames.get(iFile);
      String name = name0;
      int itype = urlTypeIndex(name);
      if (isLocal == (itype < 0 || itype == URL_LOCAL)) {
        int pt = (noPath ? -1 : name.indexOf("/" + dataPath + "/"));
        if (pt >= 0) {
          name = name.substring(pt + 1);
        } else {
          pt = name.lastIndexOf("/");
          if (pt < 0 && !noPath)
            name = "/" + name;
          if (pt < 0 || noPath)
            pt++;
          name = dataPath + name.substring(pt);
        }
      }
      Logger.info("FileManager substituting " + name0 + " --> " + name);
      oldFileNames.add("\"" + name0 + "\"");
      newFileNames.add("\1\"" + name + "\"");
    }
    return TextFormat.replaceStrings(script, oldFileNames, newFileNames);
  }

  private static String[] scriptFilePrefixes = new String[] { "/*file*/\"", "FILE0=\"", "FILE1=\"" };
  public static void getFileReferences(String script, List fileList) {
    for (int ipt = 0; ipt < scriptFilePrefixes.length; ipt++) {
      String tag = scriptFilePrefixes[ipt];
      int i = -1;
      while ((i = script.indexOf(tag, i + 1)) >= 0) {
        String s = Parser.getNextQuotedString(script, i);
        if (s.indexOf("::") >= 0)
          s = TextFormat.split(s, "::")[1];
        fileList.add(s);
      }
    }
  }

  String createZipSet(String fileName, String script, boolean includeRemoteFiles) {
    List v = new ArrayList();
    List fileNames = new ArrayList();
    getFileReferences(script, fileNames);
    List newFileNames = new ArrayList();
    int nFiles = fileNames.size();
    fileName = fileName.replace('\\', '/');
    String fileRoot = fileName.substring(fileName.lastIndexOf("/") + 1);
    if (fileRoot.indexOf(".") >= 0)
      fileRoot = fileRoot.substring(0, fileRoot.indexOf("."));
    for (int iFile = 0; iFile < nFiles; iFile++) {
      String name = (String) fileNames.get(iFile);
      int itype = urlTypeIndex(name);
      boolean isLocal = (itype < 0 || itype == URL_LOCAL);
      if (isLocal || includeRemoteFiles) {
        v.add(name)
        int pt = Math.max(name.lastIndexOf("|"), name.lastIndexOf("/"));
        String newName = "$SCRIPT_PATH$/" + name.substring(pt + 1);
        if (isLocal && name.indexOf("|") < 0) {
          v.add(null); // data will be gotten from disk
        } else {
          Object ret = getFileAsBytes(name, null);
          if (!(ret instanceof byte[]))
            return (String) ret;
          v.add(ret);
        }
        name = newName;
      }
      newFileNames.add(name);
    }
    String sname = fileRoot + ".spt";
    v.add("JmolManifest.txt");
    String sinfo = "# Jmol Manifest Zip Format 1.0\n"
      + "# Created "
      + DateFormat.getDateInstance().format(new Date()) + "\n"
      + "# JmolVersion " + Viewer.getJmolVersion() + "\n"
      + sname;
    v.add(sinfo.getBytes());
    script = TextFormat.replaceQuotedStrings(script, fileNames, newFileNames);
    v.add(sname);
    v.add(script.getBytes());
    Object bytes = viewer.getImageAs("PNG", -1, -1, -1, null, null,
        JmolConstants.embedScript(script));
    if (bytes instanceof byte[]) {
      v.add(fileRoot + ".png");
      v.add((byte[]) bytes);
    }
    return writeZipFile(fileName, v, false, "OK JMOL");
  }

  /**
   * generic method to create a zip file based on
   * http://www.exampledepot.com/egs/java.util.zip/CreateZip.html
   *
   * @param outFileName
   * @param fileNamesAndByteArrays
   *          List of [filename1, bytes|null, filename2, bytes|null, ...]
   * @param preservePath
   * @param msg
   * @return msg bytes filename or errorMessage
   */
  private static String writeZipFile(String outFileName,
                                    List fileNamesAndByteArrays,
                                    boolean preservePath, String msg) {
    byte[] buf = new byte[1024];
    long nBytesOut = 0;
    long nBytes = 0;
    Logger.info("creating zip file " + outFileName + "...");
    String fullFilePath = null;
    String fileList = "";
    try {
      ByteArrayOutputStream bos = (outFileName.startsWith("http://") ? new ByteArrayOutputStream() : null);
      ZipOutputStream os = new ZipOutputStream(bos == null ?
          (OutputStream) new FileOutputStream(outFileName) : bos);
      for (int i = 0; i < fileNamesAndByteArrays.size(); i += 2) {
        String fname = (String) fileNamesAndByteArrays.get(i);
        if (fname.indexOf("file:/") == 0)
          fname = fname.substring(6);
        byte[] bytes = (byte[]) fileNamesAndByteArrays.get(i + 1);
        String fnameShort = fname;
        if (!preservePath || fname.indexOf("|") >= 0) {
          int pt = Math.max(fname.lastIndexOf("|"), fname.lastIndexOf("/"));
          fnameShort = fnameShort.substring(pt + 1);
        }
        Logger.info("...adding " + fname);
        String key = ";" + fnameShort + ";";
        if (fileList.indexOf(key) >= 0) {
          Logger.info("duplicate entry");
          continue;
        }
        fileList += key;
        os.putNextEntry(new ZipEntry(fnameShort));
        if (bytes == null) {
          // get data from disk
          FileInputStream in = new FileInputStream(fname);
          int len;
          while ((len = in.read(buf)) > 0) {
            os.write(buf, 0, len);
            nBytesOut += len;
          }
          in.close();
        } else {
          // data are already in byte form
          os.write(bytes, 0, bytes.length);
          nBytesOut += bytes.length;
        }
        os.closeEntry();
      }
      os.close();
      Logger.info(nBytesOut + " bytes prior to compression");
      if (bos != null) {
        fullFilePath = outFileName;
        byte[] bytes = bos.toByteArray();
        nBytes = bytes.length;
        String ret = postByteArray(outFileName, bytes);
        if (ret != null)
          return ret;
      } else {
        File f = new File(outFileName);
        fullFilePath = f.getAbsolutePath().replace('\\','/');
        nBytes = f.length();
      }
    } catch (IOException e) {
      Logger.info(e.getMessage());
      return e.getMessage();
    }
    return msg + " " + nBytes + " " + fullFilePath;
  }

  private static String postByteArray(String outFileName, byte[] bytes) {
    //getInputStreamOrPost(outFileName, false, bytes, false, null, null);
    return null;
  }
  ////////////////// reader classes -- DOM, File, and Files /////////////

  private class DOMReader {
    private Object aDOMNode;
    Object atomSetCollection;
    Hashtable htParams;

    DOMReader(Object DOMNode, Hashtable htParams) {
      this.aDOMNode = DOMNode;
      this.htParams = htParams;
    }

    void run() {
      atomSetCollection = viewer.getModelAdapter().getAtomSetCollectionFromDOM(
          aDOMNode, htParams);
      if (atomSetCollection instanceof String)
        return;
      viewer.zap(false, true, false);
      fullPathName = fileName = nameAsGiven = "JSNode";
    }
  }

  private class FileReader {
    private String fileNameIn;
    private String fullPathNameIn;
    private String nameAsGivenIn;
    private String fileTypeIn;
    Object atomSetCollection;
    private BufferedReader reader;
    private Hashtable htParams;
    private boolean isAppend;

    FileReader(String fileName, String fullPathName, String nameAsGiven, String type,
        BufferedReader reader, Hashtable htParams, boolean isAppend) {
      fileNameIn = fileName;
      fullPathNameIn = fullPathName;
      nameAsGivenIn = nameAsGiven;
      fileTypeIn = type;
      this.reader = reader;
      this.htParams = htParams;
      this.isAppend = isAppend;
    }

    void run() {

      if (!isAppend && viewer.displayLoadErrors)
        viewer.zap(false, true, false);

      String errorMessage = null;
      Object t = null;
      if (reader == null) {
        t = getUnzippedBufferedReaderOrErrorMessageFromName(fullPathNameIn,
            true, false, false, true);
        if (t == null || t instanceof String) {
          errorMessage = (t == null ? "error opening:" + nameAsGivenIn
              : (String) t);
          if (!errorMessage.startsWith("NOTE:"))
            Logger.error("file ERROR: " + fullPathNameIn + "\n" + errorMessage);
          atomSetCollection = errorMessage;
          return;
        }
      }

      if (reader == null) {
        if (t instanceof BufferedReader) {
          reader = (BufferedReader) t;
        } else if (t instanceof ZipInputStream) {
          String name = fullPathNameIn;
          String[] subFileList = null;
          if (name.indexOf("|") >= 0)
            name = (subFileList = TextFormat.split(name, "|"))[0];
          if (subFileList != null)
            htParams.put("subFileList", subFileList);
          ZipInputStream zis = (ZipInputStream) t;
          String[] zipDirectory = getZipDirectory(name, true);
          atomSetCollection = viewer.getModelAdapter()
              .getAtomSetCollectionOrBufferedReaderFromZip(zis, name,
                  zipDirectory, htParams, false);
          try {
            zis.close();
          } catch (Exception e) {
            //
          }
        }
      }

      if (reader != null) {
        atomSetCollection = viewer.getModelAdapter()
            .getAtomSetCollectionReader(fullPathNameIn, fileTypeIn, reader,
                htParams);

        if (!(atomSetCollection instanceof String))
          atomSetCollection = viewer.getModelAdapter().getAtomSetCollection(
              atomSetCollection);
      }

      if (reader != null)
        try {
          reader.close();
        } catch (IOException e) {
          // ignore
        }

      if (atomSetCollection instanceof String)
        return;

      if (!isAppend && !viewer.displayLoadErrors)
        viewer.zap(false, true, false);

      fullPathName = fullPathNameIn;
      nameAsGiven = nameAsGivenIn;
      fileName = fileNameIn;

    }
  }

  /**
   * open a set of models residing in different files
   *
   */
  private class FilesReader implements JmolFileReaderInterface {
    private String[] fullPathNamesIn;
    private String[] namesAsGivenIn;
    private String[] fileTypesIn;
    Object atomSetCollection;
    private DataReader[] stringReaders;
    private Hashtable htParams;
    private boolean isAppend;

    FilesReader(String[] name, String[] nameAsGiven, String[] types,
        DataReader[] readers, Hashtable htParams, boolean isAppend) {
      fullPathNamesIn = name;
      namesAsGivenIn = nameAsGiven;
      fileTypesIn = types;
      stringReaders = readers;
      this.htParams = htParams;
      this.isAppend = isAppend;
    }

    void run() {
     
      if (!isAppend && viewer.displayLoadErrors)
        viewer.zap(false, true, false);

      boolean getReadersOnly = !viewer.displayLoadErrors;
      atomSetCollection = viewer.getModelAdapter().getAtomSetCollectionReaders(
          this, fullPathNamesIn, fileTypesIn, htParams, getReadersOnly);
      stringReaders = null;
      if (getReadersOnly && !(atomSetCollection instanceof String)) {
        atomSetCollection = viewer.getModelAdapter()
            .getAtomSetCollectionFromSet(atomSetCollection, null, htParams);
      }
      if (atomSetCollection instanceof String) {
        Logger.error("file ERROR: " + atomSetCollection);
        return;
      }
      if (!isAppend && !viewer.displayLoadErrors)
        viewer.zap(false, true, false);


      fullPathName = fileName = nameAsGiven = (stringReaders == null ? "file[]"
          : "String[]");
    }

    /**
     * called by SmartJmolAdapter to request another buffered reader,
     * rather than opening all the readers at once.
     *
     * @param i   the reader index
     * @return    a BufferedReader or null in the case of an error
     *
     */
    public Object getBufferedReader(int i) {
      if (stringReaders != null)
        return stringReaders[i].getBufferedReader();
      String name = fullPathNamesIn[i];
      String[] subFileList = null;
      htParams.remove("subFileList");
      if (name.indexOf("|") >= 0)
        name = (subFileList = TextFormat.split(name, "|"))[0];
      Object t = getUnzippedBufferedReaderOrErrorMessageFromName(name, true,
          false, false, true);
      if (t instanceof ZipInputStream) {
        if (subFileList != null)
          htParams.put("subFileList", subFileList);
        String[] zipDirectory = getZipDirectory(name, true);
        InputStream is = new BufferedInputStream(
            (InputStream) getInputStreamOrErrorMessageFromName(name, false, false),
            8192);
        t = viewer.getModelAdapter()
            .getAtomSetCollectionOrBufferedReaderFromZip(is, name,
                zipDirectory, htParams, true);
      }
      if (t instanceof BufferedReader)
        return (BufferedReader) t;
      return (t == null ? "error opening:" + namesAsGivenIn[i]
          : (String) t);
    }
  }
 
  /**
   * Just a simple abstract class to join a String reader and a String[]
   * reader under the same BufferedReader umbrella.
   *
   * Subclassed as StringDataReader, ArrayDataReader, and VectorDataReader
   *
   */

  abstract class DataReader extends BufferedReader {

    DataReader(Reader in) {
      super(in);
    }

    BufferedReader getBufferedReader() {
      return this;
   

    protected int readBuf(char[] buf) throws IOException {
      // not used by StringDataReader
      int nRead = 0;
      String line = readLine();
      if (line == null)
        return 0;
      int linept = 0;
      int linelen = (line == null ? -1 : line.length());
      for (int i = 0; i < buf.length && linelen >= 0; i++) {
          if (linept >= linelen) {
            linept = 0;
            buf[i] = '\n';
            line = readLine();
            linelen = (line == null ? -1 : line.length());
          } else {
            buf[i] = line.charAt(linept++);
          }
          nRead++;
      }
      return nRead;
    }
  }

  /**
   *
   * ArrayDataReader subclasses BufferedReader and overrides its
   * read, readLine, mark, and reset methods so that JmolAdapter
   * works with String[] arrays without any further adaptation.
   *
   */

  class ArrayDataReader extends DataReader {
    private String[] data;
    private int pt;
    private int len;
   
    ArrayDataReader(String[] data) {
      super(new StringReader(""));
      this.data = data;
      len = data.length;
    }

    public int read(char[] buf) throws IOException {
      return readBuf(buf);
    }
     
    public String readLine() {
      return (pt < len ? data[pt++] : null);
    }
   
    int ptMark;
    public void mark(long ptr) {
      //ignore ptr.
      ptMark = pt;
    }
   
    public void reset() {
      pt = ptMark;
    }
  }

  class StringDataReader extends DataReader {

    StringDataReader(String data) {
      super(new StringReader(data));
    }
  }

  /**
   *
   * VectorDataReader subclasses BufferedReader and overrides its
   * read, readLine, mark, and reset methods so that JmolAdapter
   * works with Vector<String> arrays without any further adaptation.
   *
   */

  class VectorDataReader extends DataReader {
    private List data;
    private int pt;
    private int len;
   
    VectorDataReader(List data) {
      super(new StringReader(""));
      this.data = data;
      len = data.size();
    }

    public int read(char[] buf) throws IOException {
      return readBuf(buf);
    }
   
    public String readLine() {
      return (pt < len ? (String) data.get(pt++) : null);
    }
   
    int ptMark;
    public void mark(long ptr) {
      //ignore ptr.
      ptMark = pt;
    }
   
    public void reset() {
      pt = ptMark;
    }
  }

  public static String fixFileNameVariables(String format, String fname) {
    String str = TextFormat.simpleReplace(format, "%FILE", fname);
    if (str.indexOf("%LC") < 0)
      return str;
    fname = fname.toLowerCase();
    str = TextFormat.simpleReplace(str, "%LCFILE", fname);
    if (fname.length() == 4)
      str = TextFormat.simpleReplace(str, "%LC13", fname.substring(1,3));
    return str;
  }

  /*
  private class MonitorInputStream extends FilterInputStream {
    private long length;
    private int position;
    private int markPosition;

    MonitorInputStream(InputStream in, long length) {
      super(in);
      this.length = length;
    }

    public int read() throws IOException {
      int nextByte = super.read();
      if (nextByte >= 0)
        ++position;
      return nextByte;
    }

    public int read(byte[] b) throws IOException {
      int cb = super.read(b);
      if (cb > 0)
        position += cb;
      return cb;
    }

    public int read(byte[] b, int off, int len) throws IOException {
      int cb = super.read(b, off, len);
      if (cb > 0)
        position += cb;
      return cb;
    }

    public long skip(long n) throws IOException {
      long cb = super.skip(n);
      // this will only work in relatively small files ... 2Gb
      position += cb;
      return cb;
    }

    public void mark(int readlimit) {
      super.mark(readlimit);
      markPosition = position;
    }

    public void reset() throws IOException {
      position = markPosition;
      super.reset();
    }

    int getPosition() {
      return position;
    }

    long getLength() {
      return length;
    }

    int getPercentageRead() {
      return (int) (position * 100 / length;
    }
  }
  */
}

 
TOP

Related Classes of org.jmol.viewer.FileManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.