1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2021-11-29 20:21:31 -0600 (Mon, 29 Nov 2021) $ 4 * $Revision: 22264 $ 5 * 6 * Copyright (C) 2003-2005 Miguel, Jmol Development Team 7 * 8 * Contact: jmol-developers@lists.sf.net 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 23 */ 24 package org.jmol.viewer; 25 26 import java.io.BufferedInputStream; 27 import java.io.BufferedReader; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.UnsupportedEncodingException; 31 import java.net.MalformedURLException; 32 import java.net.URL; 33 import java.net.URLEncoder; 34 import java.util.Hashtable; 35 import java.util.Map; 36 37 import org.jmol.adapter.readers.spartan.SpartanUtil; 38 import org.jmol.api.GenericFileInterface; 39 import org.jmol.api.Interface; 40 import org.jmol.api.JmolDomReaderInterface; 41 import org.jmol.api.JmolFilesReaderInterface; 42 import org.jmol.io.FileReader; 43 import org.jmol.io.JmolUtil; 44 import org.jmol.script.SV; 45 import org.jmol.script.T; 46 import org.jmol.util.Escape; 47 import org.jmol.util.Logger; 48 import org.jmol.viewer.Viewer.ACCESS; 49 50 import javajs.J2SIgnoreImport; 51 import javajs.api.BytePoster; 52 import javajs.util.AU; 53 import javajs.util.BArray; 54 import javajs.util.Base64; 55 import javajs.util.CompoundDocument; 56 import javajs.util.DataReader; 57 import javajs.util.LimitedLineReader; 58 import javajs.util.Lst; 59 import javajs.util.OC; 60 import javajs.util.PT; 61 import javajs.util.Rdr; 62 import javajs.util.SB; 63 64 65 @J2SIgnoreImport({Rdr.StreamReader.class}) 66 public class FileManager implements BytePoster { 67 68 public static String SIMULATION_PROTOCOL = "http://SIMULATION/"; 69 70 public Viewer vwr; 71 72 FileManager(Viewer vwr)73 FileManager(Viewer vwr) { 74 this.vwr = vwr; 75 clear(); 76 } 77 78 private SpartanUtil spartanDoc; 79 80 /** 81 * An isolated class to retrieve Spartan file data from compound documents, zip files, and directories 82 * @return a SpartanUtil 83 */ spartanUtil()84 public SpartanUtil spartanUtil() { 85 return (spartanDoc == null ? spartanDoc = ((SpartanUtil) Interface.getInterface("org.jmol.adapter.readers.spartan.SpartanUtil", vwr, "fm getSpartanUtil()")).set(this) : spartanDoc); 86 } 87 88 JmolUtil jzu; 89 getJzu()90 public JmolUtil getJzu() { 91 return (jzu == null ? jzu = (JmolUtil) Interface.getOption("io.JmolUtil", vwr, "file") : jzu); 92 } 93 clear()94 void clear() { 95 // from zap 96 setFileInfo(new String[] { vwr.getZapName() }); 97 spardirCache = null; 98 } 99 setLoadState(Map<String, Object> htParams)100 private void setLoadState(Map<String, Object> htParams) { 101 if (vwr.getPreserveState()) { 102 htParams.put("loadState", vwr.g.getLoadState(htParams)); 103 } 104 } 105 106 private String pathForAllFiles = ""; // leave private because of setPathForAllFiles 107 getPathForAllFiles()108 public String getPathForAllFiles() { 109 return pathForAllFiles; 110 } 111 setPathForAllFiles(String value)112 String setPathForAllFiles(String value) { 113 if (value.length() > 0 && !value.endsWith("/") && !value.endsWith("|")) 114 value += "/"; 115 return pathForAllFiles = value; 116 } 117 118 private String nameAsGiven = JC.ZAP_TITLE, fullPathName, lastFullPathName, lastNameAsGiven = JC.ZAP_TITLE, fileName; 119 120 /** 121 * Set fullPathName, fileName, and nameAsGiven 122 * 123 * @param fileInfo if null, replace fullPathName and nameAsGiven with last version of such 124 * 125 * 126 */ setFileInfo(String[] fileInfo)127 public void setFileInfo(String[] fileInfo) { 128 if (fileInfo == null) { 129 fullPathName = lastFullPathName; 130 nameAsGiven = lastNameAsGiven; 131 return; 132 } 133 // used by ScriptEvaluator dataFrame and load methods to temporarily save the state here 134 fullPathName = fileInfo[0]; 135 fileName = fileInfo[Math.min(1, fileInfo.length - 1)]; 136 nameAsGiven = fileInfo[Math.min(2, fileInfo.length - 1)]; 137 if (!nameAsGiven.equals(JC.ZAP_TITLE)) { 138 lastNameAsGiven = nameAsGiven; 139 lastFullPathName = fullPathName; 140 } 141 } 142 getFileInfo()143 public String[] getFileInfo() { 144 // used by ScriptEvaluator dataFrame method 145 return new String[] { fullPathName, fileName, nameAsGiven }; 146 } 147 getFullPathName(boolean orPrevious)148 public String getFullPathName(boolean orPrevious) { 149 String f =(fullPathName != null ? fullPathName : nameAsGiven); 150 return (!orPrevious || !f.equals(JC.ZAP_TITLE) ? f : lastFullPathName != null ? lastFullPathName : lastNameAsGiven); 151 } 152 getFileName()153 public String getFileName() { 154 return fileName != null ? fileName : nameAsGiven; 155 } 156 157 // for applet proxy 158 private URL appletDocumentBaseURL = null; 159 private String appletProxy; 160 getAppletDocumentBase()161 String getAppletDocumentBase() { 162 return (appletDocumentBaseURL == null ? "" : appletDocumentBaseURL.toString()); 163 } 164 setAppletContext(String documentBase)165 void setAppletContext(String documentBase) { 166 try { 167 System.out.println("setting document base to \"" + documentBase + "\""); 168 appletDocumentBaseURL = (documentBase.length() == 0 ? null : new URL((URL) null, documentBase, null)); 169 } catch (MalformedURLException e) { 170 System.out.println("error setting document base to " + documentBase); 171 } 172 } 173 setAppletProxy(String appletProxy)174 void setAppletProxy(String appletProxy) { 175 this.appletProxy = (appletProxy == null || appletProxy.length() == 0 ? null 176 : appletProxy); 177 } 178 179 180 /////////////// createAtomSetCollectionFromXXX methods ///////////////// 181 182 // where XXX = File, Files, String, Strings, ArrayData, DOM, Reader 183 184 /* 185 * note -- createAtomSetCollectionFromXXX methods 186 * were "openXXX" before refactoring 11/29/2008 -- BH 187 * 188 * The problem was that while they did open the file, they 189 * (mostly) also closed them, and this was confusing. 190 * 191 * The term "clientFile" was replaced by "atomSetCollection" 192 * here because that's what it is --- an AtomSetCollection, 193 * not a file. The file is closed at this point. What is 194 * returned is the atomSetCollection object. 195 * 196 * One could say this is just semantics, but there were 197 * subtle bugs here, where readers were not always being 198 * closed explicitly. In the process of identifying Out of 199 * Memory Errors, I felt it was necessary to clarify all this. 200 * 201 * Apologies to those who feel the original clientFile notation 202 * was more generalizable or understandable. 203 * 204 */ createAtomSetCollectionFromFile(String name, Map<String, Object> htParams, boolean isAppend)205 Object createAtomSetCollectionFromFile(String name, 206 Map<String, Object> htParams, 207 boolean isAppend) { 208 if (htParams.get("atomDataOnly") == null) 209 setLoadState(htParams); 210 String name0 = name; 211 name = vwr.resolveDatabaseFormat(name); 212 if (!name0.equals(name) && name0.indexOf("/") < 0 213 && Viewer.hasDatabasePrefix(name0)) { 214 htParams.put("dbName", name0); 215 } 216 if (name.endsWith("%2D%")) { 217 String filter = (String) htParams.get("filter"); 218 htParams.put("filter", (filter == null ? "" : filter) + "2D"); 219 name = name.substring(0, name.length() - 4); 220 } 221 222 int pt = name.indexOf("::"); 223 String nameAsGiven = (pt >= 0 ? name.substring(pt + 2) : name); 224 String fileType = (pt >= 0 ? name.substring(0, pt) : null); 225 Logger.info("\nFileManager.getAtomSetCollectionFromFile(" + nameAsGiven 226 + ")" + (name.equals(nameAsGiven) ? "" : " //" + name)); 227 String[] names = getClassifiedName(nameAsGiven, true); 228 if (names.length == 1) 229 return names[0]; 230 String fullPathName = names[0]; 231 String fileName = names[1]; 232 htParams.put("fullPathName", (fileType == null ? "" : fileType + "::") 233 + fixDOSName(fullPathName)); 234 if (vwr.getBoolean(T.messagestylechime) && vwr.getBoolean(T.debugscript)) 235 vwr.getChimeMessenger().update(fullPathName); 236 FileReader fileReader = new FileReader(vwr, fileName, fullPathName, nameAsGiven, 237 fileType, null, htParams, isAppend); 238 fileReader.run(); 239 return fileReader.getAtomSetCollection(); 240 } 241 createAtomSetCollectionFromFiles(String[] fileNames, Map<String, Object> htParams, boolean isAppend)242 Object createAtomSetCollectionFromFiles(String[] fileNames, 243 Map<String, Object> htParams, 244 boolean isAppend) { 245 setLoadState(htParams); 246 String[] fullPathNames = new String[fileNames.length]; 247 String[] namesAsGiven = new String[fileNames.length]; 248 String[] fileTypes = new String[fileNames.length]; 249 for (int i = 0; i < fileNames.length; i++) { 250 int pt = fileNames[i].indexOf("::"); 251 String nameAsGiven = (pt >= 0 ? fileNames[i].substring(pt + 2) 252 : fileNames[i]); 253 String fileType = (pt >= 0 ? fileNames[i].substring(0, pt) : null); 254 String[] names = getClassifiedName(nameAsGiven, true); 255 if (names.length == 1) 256 return names[0]; 257 fullPathNames[i] = names[0]; 258 fileNames[i] = fixDOSName(names[0]); 259 fileTypes[i] = fileType; 260 namesAsGiven[i] = nameAsGiven; 261 } 262 htParams.put("fullPathNames", fullPathNames); 263 htParams.put("fileTypes", fileTypes); 264 JmolFilesReaderInterface filesReader = newFilesReader(fullPathNames, namesAsGiven, 265 fileTypes, null, htParams, isAppend); 266 filesReader.run(); 267 return filesReader.getAtomSetCollection(); 268 } 269 createAtomSetCollectionFromString(String strModel, Map<String, Object> htParams, boolean isAppend)270 Object createAtomSetCollectionFromString(String strModel, 271 Map<String, Object> htParams, 272 boolean isAppend) { 273 setLoadState(htParams); 274 boolean isAddH = (strModel.indexOf(JC.ADD_HYDROGEN_TITLE) >= 0); 275 String[] fnames = (isAddH ? getFileInfo() : null); 276 FileReader fileReader = new FileReader(vwr, "string", null, null, null, 277 Rdr.getBR(strModel), htParams, isAppend); 278 fileReader.run(); 279 if (fnames != null) 280 setFileInfo(fnames); 281 if (!isAppend && !(fileReader.getAtomSetCollection() instanceof String)) { 282 // zap is unnecessary - it was done already in FileReader, and it 283 // inappropriately clears the PDB chain name map 284 // vwr.zap(false, true, false); 285 setFileInfo(new String[] { strModel == JC.MODELKIT_ZAP_STRING ? JC.MODELKIT_ZAP_TITLE 286 : "string"}); 287 } 288 return fileReader.getAtomSetCollection(); 289 } 290 createAtomSeCollectionFromStrings(String[] arrayModels, SB loadScript, Map<String, Object> htParams, boolean isAppend)291 Object createAtomSeCollectionFromStrings(String[] arrayModels, 292 SB loadScript, 293 Map<String, Object> htParams, 294 boolean isAppend) { 295 if (!htParams.containsKey("isData")) { 296 String oldSep = "\"" + vwr.getDataSeparator() + "\""; 297 String tag = "\"" + (isAppend ? "append" : "model") + " inline\""; 298 SB sb = new SB(); 299 sb.append("set dataSeparator \"~~~next file~~~\";\ndata ").append(tag); 300 for (int i = 0; i < arrayModels.length; i++) { 301 if (i > 0) 302 sb.append("~~~next file~~~"); 303 sb.append(arrayModels[i]); 304 } 305 sb.append("end ").append(tag).append(";set dataSeparator ") 306 .append(oldSep); 307 loadScript.appendSB(sb); 308 } 309 setLoadState(htParams); 310 Logger.info("FileManager.getAtomSetCollectionFromStrings(string[])"); 311 String[] fullPathNames = new String[arrayModels.length]; 312 DataReader[] readers = new DataReader[arrayModels.length]; 313 for (int i = 0; i < arrayModels.length; i++) { 314 fullPathNames[i] = "string[" + i + "]"; 315 readers[i] = newDataReader(vwr, arrayModels[i]); 316 } 317 JmolFilesReaderInterface filesReader = newFilesReader(fullPathNames, fullPathNames, 318 null, readers, htParams, isAppend); 319 filesReader.run(); 320 return filesReader.getAtomSetCollection(); 321 } 322 createAtomSeCollectionFromArrayData(Lst<Object> arrayData, Map<String, Object> htParams, boolean isAppend)323 Object createAtomSeCollectionFromArrayData(Lst<Object> arrayData, 324 Map<String, Object> htParams, 325 boolean isAppend) { 326 // NO STATE SCRIPT -- HERE WE ARE TRYING TO CONSERVE SPACE 327 Logger.info("FileManager.getAtomSetCollectionFromArrayData(Vector)"); 328 int nModels = arrayData.size(); 329 String[] fullPathNames = new String[nModels]; 330 DataReader[] readers = new DataReader[nModels]; 331 for (int i = 0; i < nModels; i++) { 332 fullPathNames[i] = "String[" + i + "]"; 333 readers[i] = newDataReader(vwr, arrayData.get(i)); 334 } 335 JmolFilesReaderInterface filesReader = newFilesReader(fullPathNames, fullPathNames, 336 null, readers, htParams, isAppend); 337 filesReader.run(); 338 return filesReader.getAtomSetCollection(); 339 } 340 newDataReader(Viewer vwr, Object data)341 static DataReader newDataReader(Viewer vwr, Object data) { 342 String reader = (data instanceof String ? "String" 343 : AU.isAS(data) ? "Array" 344 : data instanceof Lst<?> ? "List" : null); 345 if (reader == null) 346 return null; 347 DataReader dr = (DataReader) Interface.getInterface("javajs.util." + reader + "DataReader", vwr, "file"); 348 return dr.setData(data); 349 } 350 newFilesReader(String[] fullPathNames, String[] namesAsGiven, String[] fileTypes, DataReader[] readers, Map<String, Object> htParams, boolean isAppend)351 private JmolFilesReaderInterface newFilesReader(String[] fullPathNames, 352 String[] namesAsGiven, 353 String[] fileTypes, 354 DataReader[] readers, 355 Map<String, Object> htParams, 356 boolean isAppend) { 357 JmolFilesReaderInterface fr = (JmolFilesReaderInterface) Interface 358 .getOption("io.FilesReader", vwr, "file"); 359 fr.set(this, vwr, fullPathNames, namesAsGiven, fileTypes, readers, htParams, 360 isAppend); 361 return fr; 362 } 363 createAtomSetCollectionFromDOM(Object DOMNode, Map<String, Object> htParams)364 Object createAtomSetCollectionFromDOM(Object DOMNode, 365 Map<String, Object> htParams) { 366 JmolDomReaderInterface aDOMReader = (JmolDomReaderInterface) Interface.getOption("io.DOMReader", vwr, "file"); 367 aDOMReader.set(this, vwr, DOMNode, htParams); 368 aDOMReader.run(); 369 return aDOMReader.getAtomSetCollection(); 370 } 371 372 /** 373 * not used in Jmol project -- will close reader 374 * 375 * @param fullPathName 376 * @param name 377 * @param reader could be a Reader, or a BufferedInputStream or byte[] 378 * @param htParams 379 * @return fileData 380 */ createAtomSetCollectionFromReader(String fullPathName, String name, Object reader, Map<String, Object> htParams)381 Object createAtomSetCollectionFromReader(String fullPathName, String name, 382 Object reader, 383 Map<String, Object> htParams) { 384 FileReader fileReader = new FileReader(vwr, name, fullPathName, null, null, 385 reader, htParams, false); 386 fileReader.run(); 387 return fileReader.getAtomSetCollection(); 388 } 389 390 /////////////// generally useful file I/O methods ///////////////// 391 392 // mostly internal to FileManager and its enclosed classes 393 getBufferedInputStream(String fullPathName)394 BufferedInputStream getBufferedInputStream(String fullPathName) { 395 Object ret = getBufferedReaderOrErrorMessageFromName(fullPathName, 396 new String[2], true, true); 397 return (ret instanceof BufferedInputStream ? (BufferedInputStream) ret 398 : null); 399 } 400 getBufferedInputStreamOrErrorMessageFromName(String name, String fullName, boolean showMsg, boolean checkOnly, byte[] outputBytes, boolean allowReader, boolean allowCached)401 public Object getBufferedInputStreamOrErrorMessageFromName(String name, 402 String fullName, 403 boolean showMsg, 404 boolean checkOnly, 405 byte[] outputBytes, 406 boolean allowReader, 407 boolean allowCached) { 408 BufferedInputStream bis = null; 409 Object ret = null; 410 String errorMessage = null; 411 byte[] cacheBytes = (allowCached && outputBytes == null ? cacheBytes = getPngjOrDroppedBytes( 412 fullName, name) : null); 413 try { 414 if (allowCached && name.indexOf(".png") >= 0 && pngjCache == null 415 && !vwr.getBoolean(T.testflag1)) 416 pngjCache = new Hashtable<String, Object>(); 417 if (cacheBytes == null) { 418 boolean isPngjBinaryPost = (name.indexOf("?POST?_PNGJBIN_") >= 0); 419 boolean isPngjPost = (isPngjBinaryPost || name.indexOf("?POST?_PNGJ_") >= 0); 420 if (name.indexOf("?POST?_PNG_") > 0 || isPngjPost) { 421 String[] errMsg = new String[1]; 422 byte[] bytes = vwr.getImageAsBytes(isPngjPost ? "PNGJ" : "PNG", 0, 0, 423 -1, errMsg); 424 if (errMsg[0] != null) 425 return errMsg[0]; 426 if (isPngjBinaryPost) { 427 outputBytes = bytes; 428 name = PT.rep(name, "?_", "=_"); 429 } else { 430 name = new SB().append(name).append("=") 431 .appendSB(Base64.getBase64(bytes)).toString(); 432 } 433 } 434 int iurl = OC.urlTypeIndex(name); 435 boolean isURL = (iurl >= 0); 436 String post = null; 437 if (isURL && (iurl = name.indexOf("?POST?")) >= 0) { 438 post = name.substring(iurl + 6); 439 name = name.substring(0, iurl); 440 } 441 boolean isApplet = (appletDocumentBaseURL != null); 442 if (isApplet || isURL) { 443 if (isApplet && isURL && appletProxy != null) 444 name = appletProxy + "?url=" + urlEncode(name); 445 URL url = (isApplet ? new URL(appletDocumentBaseURL, name, null) 446 : new URL((URL) null, name, null)); 447 if (checkOnly) 448 return null; 449 name = url.toString(); 450 if (showMsg && name.toLowerCase().indexOf("password") < 0) 451 Logger.info("FileManager opening url " + name); 452 // note that in the case of JS, this is a javajs.util.SB. 453 ret = vwr.apiPlatform.getURLContents(url, outputBytes, post, false); 454 // if ((ret instanceof SB && ((SB) ret).length() < 3 455 // || ret instanceof String && ((String) ret).startsWith("java.")) 456 // && name.startsWith("http://ves-hx-89.ebi.ac.uk")) { 457 // // temporary bypass for EBI firewalled development server 458 // // defaulting to current directory and JSON file 459 // name = "http://chemapps.stolaf.edu/jmol/jsmol/data/" 460 // + name.substring(name.lastIndexOf("/") + 1) 461 // + (name.indexOf("/val") >= 0 ? ".val" : ".ann") + ".json"; 462 // ret = getBufferedInputStreamOrErrorMessageFromName(name, fullName, 463 // showMsg, checkOnly, outputBytes, allowReader, allowCached); 464 // } 465 466 byte[] bytes = null; 467 if (ret instanceof SB) { 468 SB sb = (SB) ret; 469 if (allowReader && !Rdr.isBase64(sb)) 470 return Rdr.getBR(sb.toString()); 471 bytes = Rdr.getBytesFromSB(sb); 472 } else if (AU.isAB(ret)) { 473 bytes = (byte[]) ret; 474 } 475 if (bytes != null) 476 ret = Rdr.getBIS(bytes); 477 } else if (!allowCached 478 || (cacheBytes = (byte[]) cacheGet(name, true)) == null) { 479 if (showMsg) 480 Logger.info("FileManager opening file " + name); 481 ret = vwr.apiPlatform.getBufferedFileInputStream(name); 482 } 483 if (ret instanceof String) 484 return ret; 485 } 486 bis = (cacheBytes == null ? (BufferedInputStream) ret : Rdr 487 .getBIS(cacheBytes)); 488 if (checkOnly) { 489 bis.close(); 490 bis = null; 491 } 492 return bis; 493 } catch (Exception e) { 494 try { 495 if (bis != null) 496 bis.close(); 497 } catch (IOException e1) { 498 } 499 errorMessage = "" + e; 500 } 501 return errorMessage; 502 } 503 504 @SuppressWarnings("null") getBufferedReaderForResource(Viewer vwr, Object resourceClass, String classPath, String resourceName)505 public static BufferedReader getBufferedReaderForResource(Viewer vwr, 506 Object resourceClass, 507 String classPath, 508 String resourceName) 509 throws IOException { 510 511 URL url; 512 /** 513 * @j2sNative 514 * 515 */ 516 { 517 url = resourceClass.getClass().getResource(resourceName); 518 if (url == null) { 519 System.err.println("Couldn't find file: " + classPath + resourceName); 520 throw new IOException(); 521 } 522 if (vwr == null || !vwr.async) 523 return Rdr.getBufferedReader( 524 new BufferedInputStream((InputStream) url.getContent()), null); 525 } 526 // applet only 527 resourceName = (url == null 528 ? vwr.vwrOptions.get("codePath") + classPath + resourceName 529 : url.getFile()); 530 if (vwr.async) { 531 // if we are running asynchronously, this will be a problem. 532 Object bytes = vwr.fm.cacheGet(resourceName, false); 533 if (bytes == null) 534 throw new JmolAsyncException(resourceName); 535 return Rdr.getBufferedReader(Rdr.getBIS((byte[]) bytes), null); 536 } 537 // JavaScript only; here and not in JavaDoc to preserve Eclipse search reference 538 return (BufferedReader) vwr.fm.getBufferedReaderOrErrorMessageFromName( 539 resourceName, new String[] { null, null }, false, true); 540 } 541 urlEncode(String name)542 private String urlEncode(String name) { 543 try { 544 return URLEncoder.encode(name, "utf-8"); 545 } catch (UnsupportedEncodingException e) { 546 return name; 547 } 548 } 549 550 /** 551 * just check for a file as being readable. Do not go into a zip file 552 * 553 * @param filename 554 * @param getStream 555 * @param ret 556 * @return String[2] where [0] is fullpathname and [1] is error message or null 557 */ getFullPathNameOrError(String filename, boolean getStream, String[] ret)558 Object getFullPathNameOrError(String filename, boolean getStream, String[] ret) { 559 String[] names = getClassifiedName(JC.fixProtocol(filename), true); 560 if (names == null || names[0] == null || names.length < 2) 561 return new String[] { null, "cannot read file name: " + filename }; 562 String name = names[0]; 563 String fullPath = fixDOSName(names[0]); 564 name = Rdr.getZipRoot(name); 565 Object errMsg = getBufferedInputStreamOrErrorMessageFromName(name, fullPath, false, !getStream, null, false, !getStream); 566 ret[0] = fullPath; 567 if (errMsg instanceof String) 568 ret[1] = (String) errMsg; 569 return errMsg; 570 } 571 getBufferedReaderOrErrorMessageFromName(String name, String[] fullPathNameReturn, boolean isBinary, boolean doSpecialLoad)572 public Object getBufferedReaderOrErrorMessageFromName(String name, 573 String[] fullPathNameReturn, 574 boolean isBinary, 575 boolean doSpecialLoad) { 576 name = JC.fixProtocol(name); 577 Object data = cacheGet(name, false); 578 boolean isBytes = AU.isAB(data); 579 byte[] bytes = (isBytes ? (byte[]) data : null); 580 if (name.startsWith("cache://")) { 581 if (data == null) 582 return "cannot read " + name; 583 if (isBytes) { 584 bytes = (byte[]) data; 585 } else { 586 return Rdr.getBR((String) data); 587 } 588 } 589 String[] names = getClassifiedName(name, true); 590 if (names == null) 591 return "cannot read file name: " + name; 592 if (fullPathNameReturn != null) 593 fullPathNameReturn[0] = fixDOSName(names[0]); 594 return getUnzippedReaderOrStreamFromName(names[0], bytes, 595 false, isBinary, false, doSpecialLoad, null); 596 } 597 598 /** 599 * 600 * @param name 601 * @param bytesOrStream 602 * cached bytes or a BufferedInputStream 603 * @param allowZipStream 604 * if the file is a zip file, allow a return that is a ZipInputStream 605 * @param forceInputStream 606 * always return a raw BufferedInputStream, not a BufferedReader, and 607 * do not process PNGJ files 608 * @param isTypeCheckOnly 609 * when possibly reading a spartan file for content (doSpecialLoad == 610 * true), just return the compound document's file list 611 * @param doSpecialLoad 612 * check for a Spartan file 613 * @param htParams 614 * @return String if error or String[] if a type check or BufferedReader or 615 * BufferedInputStream 616 */ 617 @SuppressWarnings("resource") getUnzippedReaderOrStreamFromName(String name, Object bytesOrStream, boolean allowZipStream, boolean forceInputStream, boolean isTypeCheckOnly, boolean doSpecialLoad, Map<String, Object> htParams)618 public Object getUnzippedReaderOrStreamFromName(String name, 619 Object bytesOrStream, 620 boolean allowZipStream, 621 boolean forceInputStream, 622 boolean isTypeCheckOnly, 623 boolean doSpecialLoad, 624 Map<String, Object> htParams) { 625 if (doSpecialLoad && bytesOrStream == null) { 626 Object o = (name.endsWith(".spt") ? new String[] { null, null, null } // DO NOT actually load any file 627 : name.indexOf(".spardir") < 0 ? null 628 : spartanUtil().getFileList(name, isTypeCheckOnly)); 629 if (o != null) 630 return o; 631 } 632 name = JC.fixProtocol(name); 633 if (bytesOrStream == null 634 && (bytesOrStream = getCachedPngjBytes(name)) != null 635 && htParams != null) 636 htParams.put("sourcePNGJ", Boolean.TRUE); 637 name = name.replace("#_DOCACHE_", ""); 638 String fullName = name; 639 String[] subFileList = null; 640 if (name.indexOf("|") >= 0) { 641 subFileList = PT.split(name.replace('\\', '/'), "|"); 642 if (bytesOrStream == null) 643 Logger.info("FileManager opening zip " + name); 644 name = subFileList[0]; 645 } 646 Object t = (bytesOrStream == null ? getBufferedInputStreamOrErrorMessageFromName( 647 name, fullName, true, false, null, !forceInputStream, true) : AU 648 .isAB(bytesOrStream) ? Rdr.getBIS((byte[]) bytesOrStream) 649 : (BufferedInputStream) bytesOrStream); 650 try { 651 if (t instanceof String || t instanceof BufferedReader) 652 return t; 653 BufferedInputStream bis = (BufferedInputStream) t; 654 if (Rdr.isGzipS(bis)) 655 bis = Rdr.getUnzippedInputStream(vwr.getJzt(), bis); 656 // if we have a subFileList, we don't want to return the stream for the zip file itself 657 else if (Rdr.isBZip2S(bis)) 658 bis = Rdr.getUnzippedInputStreamBZip2(vwr.getJzt(), bis); 659 // if we have a subFileList, we don't want to return the stream for the zip file itself 660 if (Rdr.isTar(bis)) { 661 Object o = vwr.getJzt().getZipFileDirectory(bis, subFileList, 1, 662 forceInputStream); 663 return (o instanceof String ? Rdr.getBR((String) o) : o); 664 } 665 666 if (forceInputStream && subFileList == null) 667 return bis; 668 if (Rdr.isCompoundDocumentS(bis)) { 669 // very specialized reader; assuming we have a Spartan document here 670 CompoundDocument doc = (CompoundDocument) Interface.getInterface( 671 "javajs.util.CompoundDocument", vwr, "file"); 672 doc.setDocStream(vwr.getJzt(), bis); 673 String s = doc.getAllDataFiles("Molecule", "Input").toString(); 674 return (forceInputStream ? Rdr.getBIS(s.getBytes()) : Rdr.getBR(s)); 675 } 676 // check for PyMOL or MMTF 677 if (Rdr.isMessagePackS(bis) || Rdr.isPickleS(bis)) 678 return bis; 679 bis = Rdr.getPngZipStream(bis, true); 680 if (Rdr.isZipS(bis)) { 681 if (allowZipStream) 682 return vwr.getJzt().newZipInputStream(bis); 683 Object o = vwr.getJzt().getZipFileDirectory(bis, subFileList, 1, 684 forceInputStream); 685 return (o instanceof String ? Rdr.getBR((String) o) : o); 686 } 687 return (forceInputStream ? bis : Rdr.getBufferedReader(bis, null)); 688 } catch (Exception ioe) { 689 return ioe.toString(); 690 } 691 } 692 693 694 /** 695 * 696 * @param fileName 697 * @param addManifest 698 * @param allowCached 699 * @return [] if not a zip file; 700 */ getZipDirectory(String fileName, boolean addManifest, boolean allowCached)701 public String[] getZipDirectory(String fileName, boolean addManifest, boolean allowCached) { 702 Object t = getBufferedInputStreamOrErrorMessageFromName(fileName, fileName, 703 false, false, null, false, allowCached); 704 return vwr.getJzt().getZipDirectoryAndClose((BufferedInputStream) t, addManifest ? "JmolManifest" : null); 705 } 706 getFileAsBytes(String name, OC out)707 public Object getFileAsBytes(String name, OC out) { 708 // used by OutputManager.createZipSet" 709 // will be full path name 710 if (name == null) 711 return null; 712 String fullName = name; 713 String[] subFileList = null; 714 if (name.indexOf("|") >= 0) { 715 subFileList = PT.split(name, "|"); 716 name = subFileList[0]; 717 } 718 // JavaScript will use this method to load a cached file, 719 // but in that case we can get the bytes directly and not 720 // fool with a BufferedInputStream, and we certainly do not want to 721 // open it twice in the case of the returned interior file being another PNGJ file 722 Object bytes = (subFileList != null ? null : getPngjOrDroppedBytes(fullName, name)); 723 if (bytes == null) { 724 Object t = getBufferedInputStreamOrErrorMessageFromName(name, fullName, 725 false, false, null, false, true); 726 if (t instanceof String) 727 return "Error:" + t; 728 try { 729 BufferedInputStream bis = (BufferedInputStream) t; 730 bytes = (out != null || subFileList == null || subFileList.length <= 1 731 || !Rdr.isZipS(bis) && !Rdr.isPngZipStream(bis) 732 && !Rdr.isTar(bis) 733 ? Rdr.getStreamAsBytes(bis, out) 734 : vwr.getJzt().getZipFileContentsAsBytes(bis, subFileList, 1)); 735 736 bis.close(); 737 } catch (Exception ioe) { 738 return ioe.toString(); 739 } 740 } 741 if (out == null || !AU.isAB(bytes)) 742 return bytes; 743 out.write((byte[]) bytes, 0, -1); 744 return ((byte[]) bytes).length + " bytes"; 745 } 746 getFileAsMap(String name, String type)747 public Map<String, Object> getFileAsMap(String name, String type) { 748 Map<String, Object> bdata = new Hashtable<String, Object>(); 749 Object t; 750 if (name == null) { 751 // return the current state as a PNGJ or ZIPDATA 752 String[] errMsg = new String[1]; 753 byte[] bytes = vwr.getImageAsBytes(type, -1, -1, -1, errMsg); 754 if (errMsg[0] != null) { 755 bdata.put("_ERROR_", errMsg[0]); 756 return bdata; 757 } 758 t = Rdr.getBIS(bytes); 759 } else { 760 String[] data = new String[2]; 761 t = getFullPathNameOrError(name, true, data); 762 if (t instanceof String) { 763 bdata.put("_ERROR_", t); 764 return bdata; 765 } 766 if (!checkSecurity(data[0])) { 767 bdata.put("_ERROR_", "java.io. Security exception: cannot read file " 768 + data[0]); 769 return bdata; 770 } 771 } 772 try { 773 vwr.getJzt().readFileAsMap((BufferedInputStream) t, bdata, name); 774 775 } catch (Exception e) { 776 bdata.clear(); 777 bdata.put("_ERROR_", "" + e); 778 } 779 return bdata; 780 } 781 782 /** 783 * 784 * @param data 785 * [0] initially path name, but returned as full path name; [1]file 786 * contents (directory listing for a ZIP/JAR file) or error string 787 * @param nBytesMax 788 * or -1 789 * @param doSpecialLoad 790 * @param allowBinary 791 * @param checkProtected 792 * TODO 793 * @return true if successful; false on error 794 */ 795 getFileDataAsString(String[] data, int nBytesMax, boolean doSpecialLoad, boolean allowBinary, boolean checkProtected)796 public boolean getFileDataAsString(String[] data, int nBytesMax, 797 boolean doSpecialLoad, 798 boolean allowBinary, boolean checkProtected) { 799 data[1] = ""; 800 String name = data[0]; 801 if (name == null) 802 return false; 803 Object t = getBufferedReaderOrErrorMessageFromName(name, data, false, 804 doSpecialLoad); 805 if (t instanceof String) { 806 data[1] = (String) t; 807 return false; 808 } 809 if (checkProtected && !checkSecurity(data[0])) { 810 data[1] = "java.io. Security exception: cannot read file " + data[0]; 811 return false; 812 } 813 try { 814 return Rdr.readAllAsString((BufferedReader) t, nBytesMax, allowBinary, 815 data, 1); 816 } catch (Exception e) { 817 return false; 818 } 819 } 820 checkSecurity(String f)821 private boolean checkSecurity(String f) { 822 // when load() function and local file: 823 if (!f.startsWith("file:")) 824 return true; 825 int pt = f.lastIndexOf('/'); 826 // root directory C:/foo or file:///c:/foo or "/foo" 827 // no hidden files 828 // no files without extension 829 if (f.lastIndexOf(":/") == pt - 1 830 || f.indexOf("/.") >= 0 831 || f.lastIndexOf('.') < f.lastIndexOf('/')) 832 return false; 833 return true; 834 } 835 836 /** 837 * Load an image 838 * @param nameOrBytes 839 * @param echoName 840 * @param forceSync TODO 841 * @return true if asynchronous 842 */ 843 @SuppressWarnings("unchecked") loadImage(Object nameOrBytes, String echoName, boolean forceSync)844 public boolean loadImage(Object nameOrBytes, String echoName, boolean forceSync) { 845 Object image = null; 846 String nameOrError = null; 847 byte[] bytes = null; 848 boolean isPopupImage = (echoName != null && echoName.startsWith("\1")); 849 if (isPopupImage) { 850 if (echoName.equals("\1closeall\1null")) 851 return vwr.loadImageData(Boolean.TRUE, "\1closeall", "\1closeall", null); 852 if ("\1close".equals(nameOrBytes)) 853 return vwr.loadImageData(Boolean.FALSE, "\1close", echoName, null); 854 } 855 if (nameOrBytes instanceof Map) { 856 nameOrBytes = (((Map<String, Object>) nameOrBytes).containsKey("_DATA_") ? ((Map<String, Object>) nameOrBytes) 857 .get("_DATA_") : ((Map<String, Object>) nameOrBytes).get("_IMAGE_")); 858 } 859 if (nameOrBytes instanceof SV) 860 nameOrBytes = ((SV) nameOrBytes).value; 861 String name = (nameOrBytes instanceof String ? (String) nameOrBytes : null); 862 boolean isAsynchronous = false; 863 if (name != null && name.startsWith(";base64,")) { 864 bytes = Base64.decodeBase64(name); 865 } else if (nameOrBytes instanceof BArray) { 866 bytes = ((BArray) nameOrBytes).data; 867 } else if (echoName == null || nameOrBytes instanceof String) { 868 String[] names = getClassifiedName((String) nameOrBytes, true); 869 nameOrError = (names == null ? "cannot read file name: " + nameOrBytes 870 : fixDOSName(names[0])); 871 if (names != null) 872 image = getImage(nameOrError, echoName, forceSync); 873 isAsynchronous = (image == null); 874 } else { 875 image = nameOrBytes; 876 } 877 if (bytes != null) { 878 image = getImage(bytes, echoName, true); 879 isAsynchronous = false; 880 } 881 if (image instanceof String) { 882 nameOrError = (String) image; 883 image = null; 884 } 885 if (!Viewer.isJS && image != null && bytes != null) 886 nameOrError = ";base64," + Base64.getBase64(bytes).toString(); 887 if (!Viewer.isJS || isPopupImage && nameOrError == null 888 || !isPopupImage && image != null) 889 return vwr.loadImageData(image, nameOrError, echoName, null); 890 return isAsynchronous; 891 // JSmol will call that from awtjs2d.Platform.java asynchronously 892 } 893 getImage(Object nameOrBytes, String echoName, boolean forceSync)894 public Object getImage(Object nameOrBytes, String echoName, 895 boolean forceSync) { 896 return getJzu().getImage(vwr, nameOrBytes, echoName, forceSync); 897 } 898 899 /** 900 * [0] and [2] may return same as [1] in the 901 * case of a local unsigned applet. 902 * 903 * @param name 904 * @param isFullLoad 905 * false only when just checking path 906 * @return [0] full path name, [1] file name without path, [2] full URL 907 */ getClassifiedName(String name, boolean isFullLoad)908 private String[] getClassifiedName(String name, boolean isFullLoad) { 909 if (name == null) 910 return new String[] { null }; 911 boolean doSetPathForAllFiles = (pathForAllFiles.length() > 0); 912 if (name.startsWith("?") || name.startsWith("http://?")|| name.startsWith("https://?")) { 913 if (!Viewer.isJS && (name = vwr.dialogAsk("Load", name, null)) == null) 914 return new String[] { isFullLoad ? "#CANCELED#" : null }; 915 doSetPathForAllFiles = false; 916 } 917 GenericFileInterface file = null; 918 URL url = null; 919 String[] names = null; 920 if (name.startsWith("cache://")) { 921 names = new String[3]; 922 names[0] = names[2] = name; 923 names[1] = stripPath(names[0]); 924 return names; 925 } 926 name = vwr.resolveDatabaseFormat(name); 927 if (name.indexOf(":") < 0 && name.indexOf("/") != 0) 928 name = addDirectory(vwr.getDefaultDirectory(), name); 929 if (appletDocumentBaseURL == null) { 930 // This code is for the app or signed local applet 931 // -- no local file reading for headless 932 if (OC.urlTypeIndex(name) >= 0 || vwr.haveAccess(ACCESS.NONE) 933 || vwr.haveAccess(ACCESS.READSPT) && !name.endsWith(".spt") 934 && !name.endsWith("/")) { 935 try { 936 url = new URL((URL) null, name, null); 937 } catch (MalformedURLException e) { 938 return new String[] { isFullLoad ? e.toString() : null }; 939 } 940 } else { 941 file = vwr.apiPlatform.newFile(name); 942 String s = file.getFullPath(); 943 // local unsigned applet may have access control issue here and get a null return 944 String fname = file.getName(); 945 names = new String[] { (s == null ? fname : s), fname, 946 (s == null ? fname : "file:/" + s.replace('\\', '/')) }; 947 } 948 } else { 949 // This code is only for the non-local applet 950 try { 951 if (name.indexOf(":\\") == 1 || name.indexOf(":/") == 1) 952 name = "file:/" + name; 953 // else if (name.indexOf("/") == 0 && vwr.isSignedApplet()) 954 // name = "file:" + name; 955 url = new URL(appletDocumentBaseURL, name, null); 956 } catch (MalformedURLException e) { 957 return new String[] { isFullLoad ? e.toString() : null }; 958 } 959 } 960 if (url != null) { 961 names = new String[3]; 962 names[0] = names[2] = url.toString(); 963 names[1] = stripPath(names[0]); 964 } 965 if (doSetPathForAllFiles) { 966 String name0 = names[0]; 967 names[0] = pathForAllFiles + names[1]; 968 Logger.info("FileManager substituting " + name0 + " --> " + names[0]); 969 } 970 if (isFullLoad && OC.isLocal(names[0])) { 971 String path = names[0]; 972 if (file == null) 973 path = PT.trim(names[0].substring(names[0].indexOf(":") + 1), "/"); 974 int pt = path.length() - names[1].length() - 1; 975 if (pt > 0) { 976 path = path.substring(0, pt); 977 setLocalPath(vwr, path, true); 978 } 979 } 980 return names; 981 } 982 983 ///// DIRECTORY BUSINESS 984 addDirectory(String defaultDirectory, String name)985 private static String addDirectory(String defaultDirectory, String name) { 986 if (defaultDirectory.length() == 0) 987 return name; 988 char ch = (name.length() > 0 ? name.charAt(0) : ' '); 989 String s = defaultDirectory.toLowerCase(); 990 if ((s.endsWith(".zip") || s.endsWith(".tar")) && ch != '|' && ch != '/') 991 defaultDirectory += "|"; 992 return defaultDirectory 993 + (ch == '/' 994 || ch == '/' 995 || (ch = defaultDirectory.charAt(defaultDirectory.length() - 1)) == '|' 996 || ch == '/' ? "" : "/") + name; 997 } 998 getDefaultDirectory(String name)999 String getDefaultDirectory(String name) { 1000 String[] names = getClassifiedName(name, true); 1001 if (names == null) 1002 return ""; 1003 name = fixPath(names[0]); 1004 return (name == null ? "" : name.substring(0, name.lastIndexOf("/"))); 1005 } 1006 fixPath(String path)1007 private static String fixPath(String path) { 1008 path = fixDOSName(path); 1009 path = PT.rep(path, "/./", "/"); 1010 int pt = path.lastIndexOf("//") + 1; 1011 if (pt < 1) 1012 pt = path.indexOf(":/") + 1; 1013 if (pt < 1) 1014 pt = path.indexOf("/"); 1015 if (pt < 0) 1016 return null; 1017 String protocol = path.substring(0, pt); 1018 path = path.substring(pt); 1019 1020 while ((pt = path.lastIndexOf("/../")) >= 0) { 1021 int pt0 = path.substring(0, pt).lastIndexOf("/"); 1022 if (pt0 < 0) 1023 return PT.rep(protocol + path, "/../", "/"); 1024 path = path.substring(0, pt0) + path.substring(pt + 3); 1025 } 1026 if (path.length() == 0) 1027 path = "/"; 1028 return protocol + path; 1029 } 1030 getFilePath(String name, boolean addUrlPrefix, boolean asShortName)1031 public String getFilePath(String name, boolean addUrlPrefix, 1032 boolean asShortName) { 1033 String[] names = getClassifiedName(name, false); 1034 return (names == null || names.length == 1 ? "" : asShortName ? names[1] 1035 : addUrlPrefix ? names[2] 1036 : names[0] == null ? "" 1037 : fixDOSName(names[0])); 1038 } 1039 getLocalDirectory(Viewer vwr, boolean forDialog)1040 public static GenericFileInterface getLocalDirectory(Viewer vwr, boolean forDialog) { 1041 String localDir = (String) vwr 1042 .getP(forDialog ? "currentLocalPath" : "defaultDirectoryLocal"); 1043 if (forDialog && localDir.length() == 0) 1044 localDir = (String) vwr.getP("defaultDirectoryLocal"); 1045 if (localDir.length() == 0) 1046 return (vwr.isApplet ? null : vwr.apiPlatform.newFile(System 1047 .getProperty("user.dir", "."))); 1048 if (vwr.isApplet && localDir.indexOf("file:/") == 0) 1049 localDir = localDir.substring(6); 1050 GenericFileInterface f = vwr.apiPlatform.newFile(localDir); 1051 try { 1052 return f.isDirectory() ? f : f.getParentAsFile(); 1053 } catch (Exception e) { 1054 return null; 1055 } 1056 } 1057 1058 /** 1059 * called by getImageFileNameFromDialog 1060 * called by getOpenFileNameFromDialog 1061 * called by getSaveFileNameFromDialog 1062 * 1063 * called by classifyName for any full file load 1064 * called from the CD command 1065 * 1066 * currentLocalPath is set in all cases 1067 * and is used specifically for dialogs as a first try 1068 * defaultDirectoryLocal is set only when not from a dialog 1069 * and is used only in getLocalPathForWritingFile or 1070 * from an open/save dialog. 1071 * In this way, saving a file from a dialog doesn't change 1072 * the "CD" directory. 1073 * Neither of these is saved in the state, but 1074 * 1075 * 1076 * @param vwr 1077 * @param path 1078 * @param forDialog 1079 */ setLocalPath(Viewer vwr, String path, boolean forDialog)1080 public static void setLocalPath(Viewer vwr, String path, 1081 boolean forDialog) { 1082 while (path.endsWith("/") || path.endsWith("\\")) 1083 path = path.substring(0, path.length() - 1); 1084 vwr.setStringProperty("currentLocalPath", path); 1085 if (!forDialog) 1086 vwr.setStringProperty("defaultDirectoryLocal", path); 1087 } 1088 getLocalPathForWritingFile(Viewer vwr, String file)1089 public static String getLocalPathForWritingFile(Viewer vwr, String file) { 1090 if (file.startsWith("http://") || file.startsWith("https://")) 1091 return file; 1092 file = PT.rep(file, "?", ""); 1093 if (file.indexOf("file:/") == 0) 1094 return file.substring(6); 1095 if (file.indexOf("/") == 0 || file.indexOf(":") >= 0) 1096 return file; 1097 GenericFileInterface dir = null; 1098 try { 1099 dir = getLocalDirectory(vwr, false); 1100 } catch (Exception e) { 1101 // access control for unsigned applet 1102 } 1103 return (dir == null ? file : fixPath(dir.toString() + "/" + file)); 1104 } 1105 1106 /** 1107 * Switch \ for / only for DOS names such as C:\temp\t.xyz, not names like http://cactus.nci.nih.gov/chemical/structure/CC/C=C\CC 1108 * @param fileName 1109 * @return fixed name 1110 */ fixDOSName(String fileName)1111 public static String fixDOSName(String fileName) { 1112 return (fileName.indexOf(":\\") >= 0 ? fileName.replace('\\', '/') : fileName); 1113 } 1114 stripPath(String name)1115 public static String stripPath(String name) { 1116 int pt = Math.max(name.lastIndexOf("|"), name.lastIndexOf("/")); 1117 return name.substring(pt + 1); 1118 } 1119 1120 ///// FILE TYPING ///// 1121 1122 private final static String DELPHI_BINARY_MAGIC_NUMBER = "\24\0\0\0"; //0x14 0 0 0 == "20-byte character string follows" 1123 public final static String PMESH_BINARY_MAGIC_NUMBER = "PM\1\0"; 1124 public static final String JPEG_CONTINUE_STRING = " #Jmol...\0"; 1125 isScriptType(String fname)1126 public static boolean isScriptType(String fname) { 1127 return PT.isOneOf(fname.toLowerCase().substring(fname.lastIndexOf(".")+1), ";pse;spt;png;pngj;jmol;zip;"); 1128 } 1129 1130 // was simplistic check from FileDropper 1131 // public static boolean isSurfaceType(String fname) { 1132 // return PT.isOneOf(fname.toLowerCase().substring(fname.lastIndexOf(".")+1), ";jvxl;kin;o;msms;map;pmesh;mrc;efvet;cube;obj;dssr;bcif;"); 1133 // } 1134 // determineSurfaceFileType(BufferedReader bufferedReader)1135 public static String determineSurfaceFileType(BufferedReader bufferedReader) { 1136 // drag-drop and isosurface command only 1137 // JVXL should be on the FIRST line of the file, but it may be 1138 // after comments or missing. 1139 1140 // Apbs, Jvxl, or Cube, also efvet and DHBD 1141 1142 String line = null; 1143 1144 if (bufferedReader instanceof Rdr.StreamReader) { 1145 BufferedInputStream is = ((Rdr.StreamReader) bufferedReader).getStream(); 1146 if (is.markSupported()) { 1147 try { 1148 is.mark(300); 1149 byte[] buf = new byte[300]; 1150 is.read(buf, 0, 300); 1151 is.reset(); 1152 if ((buf[0] & 0xFF) == 0x83) // Finite map(3) 1153 return "BCifDensity"; 1154 if (buf[0] == 'P' && buf[1] == 'M' && buf[2] == 1 && buf[3] == 0)// "PM\1\0" 1155 return "Pmesh"; 1156 if (buf[208] == 'M' && buf[209] == 'A' && buf[210] == 'P')// "MAP" at 208 1157 return "Mrc"; 1158 if (buf[0] == '\24' && buf[1] == 0 && buf[2] == 0 && buf[3] == 0) 1159 return "DelPhi"; 1160 if (buf[36] == 0 && buf[37] == 100) // header19 (short)100 1161 return "Dsn6"; 1162 } catch (IOException e) { 1163 // ignore 1164 } 1165 } 1166 } 1167 1168 LimitedLineReader br = null; 1169 1170 try { 1171 br = new LimitedLineReader(bufferedReader, 16000); 1172 line = br.getHeader(0); 1173 } catch (Exception e) { 1174 // 1175 } 1176 if (br == null || line == null || line.length() == 0) 1177 return null; 1178 1179 // binary formats: problem here is that the buffered reader 1180 // may be translating byte sequences into unicode 1181 // and thus shifting the offset 1182 int pt0 = line.indexOf('\0'); 1183 if (pt0 >= 0) { 1184 if (line.charAt(0) == 0x83) // Finite map(3) 1185 return "BCifDensity"; 1186 if (line.indexOf(PMESH_BINARY_MAGIC_NUMBER) == 0) 1187 return "Pmesh"; 1188 if (line.indexOf("MAP ") == 208) 1189 return "Mrc"; 1190 if (line.indexOf(DELPHI_BINARY_MAGIC_NUMBER) == 0) 1191 return "DelPhi"; 1192 if (line.length() > 37 1193 && (line.charAt(36) == 0 && line.charAt(37) == 100 || line.charAt(36) == 0 1194 && line.charAt(37) == 100)) { 1195 // header19 (short)100 1196 return "Dsn6"; 1197 } 1198 } 1199 1200 //for (int i = 0; i < 220; i++) 1201 // System.out.print(" " + i + ":" + (0 + line.charAt(i))); 1202 //System.out.println(""); 1203 1204 switch (line.charAt(0)) { 1205 case '@': 1206 if (line.indexOf("@text") == 0) 1207 return "Kinemage"; 1208 break; 1209 case '#': 1210 if (line.indexOf(".obj") >= 0) 1211 return "Obj"; // #file: pymol.obj 1212 if (line.indexOf("MSMS") >= 0) 1213 return "Msms"; 1214 break; 1215 case '&': 1216 if (line.indexOf("&plot") == 0) 1217 return "Jaguar"; 1218 break; 1219 case '\r': 1220 case '\n': 1221 if (line.indexOf("ZYX") >= 0) 1222 return "Xplor"; 1223 break; 1224 } 1225 if (line.indexOf("Here is your gzipped map") >= 0) 1226 return "UPPSALA" + line; 1227 if (line.startsWith("data_SERVER")) 1228 return "CifDensity"; 1229 if (line.startsWith("4MESHC")) 1230 return "Pmesh4"; 1231 if (line.indexOf("! nspins") >= 0) 1232 return "CastepDensity"; 1233 if (line.indexOf("<jvxl") >= 0 && line.indexOf("<?xml") >= 0) 1234 return "JvxlXml"; 1235 if (line.indexOf("#JVXL+") >= 0) 1236 return "Jvxl+"; 1237 if (line.indexOf("#JVXL") >= 0) 1238 return "Jvxl"; 1239 if (line.indexOf("#JmolPmesh") >= 0) 1240 return "Pmesh"; 1241 if (line.indexOf("#obj") >= 0) 1242 return "Obj"; 1243 if (line.indexOf("#pmesh") >= 0) 1244 return "Obj"; 1245 if (line.indexOf("<efvet ") >= 0) 1246 return "Efvet"; 1247 if (line.indexOf("usemtl") >= 0) 1248 return "Obj"; 1249 if (line.indexOf("# object with") == 0) 1250 return "Nff"; 1251 if (line.indexOf("BEGIN_DATAGRID_3D") >= 0 1252 || line.indexOf("BEGIN_BANDGRID_3D") >= 0) 1253 return "Xsf"; 1254 if (line.indexOf("tiles in x, y") >= 0) 1255 return "Ras3D"; 1256 if (line.indexOf(" 0.00000e+00 0.00000e+00 0 0\n") >= 0) 1257 return "Uhbd"; // older APBS http://sourceforge.net/p/apbs/code/ci/9527462a39126fb6cd880924b3cc4880ec4b78a9/tree/src/mg/vgrid.c 1258 1259 // Apbs, Jvxl, Obj, or Cube, maybe formatted Plt 1260 1261 line = br.readLineWithNewline(); 1262 if (line.indexOf("object 1 class gridpositions counts") == 0) 1263 return "Apbs"; 1264 1265 String[] tokens = PT.getTokens(line); 1266 String line2 = br.readLineWithNewline();// second line 1267 if (tokens.length == 2 && PT.parseInt(tokens[0]) == 3 1268 && PT.parseInt(tokens[1]) != Integer.MIN_VALUE) { 1269 tokens = PT.getTokens(line2); 1270 if (tokens.length == 3 && PT.parseInt(tokens[0]) != Integer.MIN_VALUE 1271 && PT.parseInt(tokens[1]) != Integer.MIN_VALUE 1272 && PT.parseInt(tokens[2]) != Integer.MIN_VALUE) 1273 return "PltFormatted"; 1274 } 1275 String line3 = br.readLineWithNewline(); // third line 1276 if (line.startsWith("v ") && line2.startsWith("v ") 1277 && line3.startsWith("v ")) 1278 return "Obj"; 1279 //next line should be the atom line 1280 int nAtoms = PT.parseInt(line3); 1281 if (nAtoms == Integer.MIN_VALUE) 1282 return (line3.indexOf("+") == 0 ? "Jvxl+" : null); 1283 tokens = PT.getTokens(line3); 1284 if (tokens[0].indexOf(".") > 0) 1285 return (line3.length() >= 60 || tokens.length != 3 ? null : "VaspChgcar"); // M40 files are > 60 char 1286 if (nAtoms >= 0) 1287 return (tokens.length == 4 || tokens.length == 5 && tokens[4].equals("1") ? "Cube" 1288 : null); //Can't be a Jvxl file; 1289 nAtoms = -nAtoms; 1290 for (int i = 4 + nAtoms; --i >= 0;) 1291 if ((line = br.readLineWithNewline()) == null) 1292 return null; 1293 int nSurfaces = PT.parseInt(line); 1294 if (nSurfaces == Integer.MIN_VALUE) 1295 return null; 1296 return (nSurfaces < 0 ? "Jvxl" : "Cube"); //Final test looks at surface definition line 1297 } 1298 1299 ///// JMOL SCRIPT FILE PROCESSING 1300 1301 /** 1302 * check a JmolManifest for a reference to a script file (.spt) 1303 * 1304 * @param manifest 1305 * @return null, "", or a directory entry in the ZIP file 1306 */ 1307 getManifestScriptPath(String manifest)1308 public static String getManifestScriptPath(String manifest) { 1309 if (manifest.indexOf("$SCRIPT_PATH$") >= 0) 1310 return ""; 1311 String ch = (manifest.indexOf('\n') >= 0 ? "\n" : "\r"); 1312 if (manifest.indexOf(".spt") >= 0) { 1313 String[] s = PT.split(manifest, ch); 1314 for (int i = s.length; --i >= 0;) 1315 if (s[i].indexOf(".spt") >= 0) 1316 return "|" + PT.trim(s[i], "\r\n \t"); 1317 } 1318 return null; 1319 } 1320 getFileReferences(String script, Lst<String> fileList, Lst<String> fileListUTF)1321 public static void getFileReferences(String script, Lst<String> fileList, 1322 Lst<String> fileListUTF) { 1323 for (int ipt = 0; ipt < scriptFilePrefixes.length; ipt++) { 1324 String tag = scriptFilePrefixes[ipt]; 1325 int i = -1; 1326 while ((i = script.indexOf(tag, i + 1)) >= 0) { 1327 String s = stripTypePrefix(PT.getQuotedStringAt(script, i)); 1328 if (s.indexOf("\\u") >= 0) 1329 s = Escape.unescapeUnicode(s); 1330 fileList.addLast(s); 1331 if (fileListUTF != null) { 1332 if (s.indexOf("\\u") >= 0) 1333 s = Escape.unescapeUnicode(s); 1334 fileListUTF.addLast(s); 1335 } 1336 } 1337 } 1338 } 1339 1340 private static String[] scriptFilePrefixes = new String[] { "/*file*/\"", 1341 "FILE0=\"", "FILE1=\"" }; 1342 setScriptFileReferences(String script, String localPath, String remotePath, String scriptPath)1343 public static String setScriptFileReferences(String script, String localPath, 1344 String remotePath, 1345 String scriptPath) { 1346 if (localPath != null) 1347 script = setScriptFileRefs(script, localPath, true); 1348 if (remotePath != null) 1349 script = setScriptFileRefs(script, remotePath, false); 1350 script = PT.rep(script, "\1\"", "\""); 1351 if (scriptPath != null) { 1352 while (scriptPath.endsWith("/")) 1353 scriptPath = scriptPath.substring(0, scriptPath.length() - 1); 1354 for (int ipt = 0; ipt < scriptFilePrefixes.length; ipt++) { 1355 String tag = scriptFilePrefixes[ipt]; 1356 script = PT.rep(script, tag + ".", tag + scriptPath); 1357 } 1358 } 1359 return script; 1360 } 1361 1362 /** 1363 * Sets all local file references in a script file to point to files within 1364 * dataPath. If a file reference contains dataPath, then the file reference is 1365 * left with that RELATIVE path. Otherwise, it is changed to a relative file 1366 * name within that dataPath. 1367 * 1368 * Only file references starting with "file://" are changed. 1369 * 1370 * @param script 1371 * @param dataPath 1372 * @param isLocal 1373 * @return revised script 1374 */ setScriptFileRefs(String script, String dataPath, boolean isLocal)1375 private static String setScriptFileRefs(String script, String dataPath, 1376 boolean isLocal) { 1377 if (dataPath == null) 1378 return script; 1379 boolean noPath = (dataPath.length() == 0); 1380 Lst<String> fileNames = new Lst<String>(); 1381 getFileReferences(script, fileNames, null); 1382 Lst<String> oldFileNames = new Lst<String>(); 1383 Lst<String> newFileNames = new Lst<String>(); 1384 int nFiles = fileNames.size(); 1385 for (int iFile = 0; iFile < nFiles; iFile++) { 1386 String name0 = fileNames.get(iFile); 1387 String name = name0; 1388 if (isLocal == OC.isLocal(name)) { 1389 int pt = (noPath ? -1 : name.indexOf("/" + dataPath + "/")); 1390 if (pt >= 0) { 1391 name = name.substring(pt + 1); 1392 } else { 1393 pt = name.lastIndexOf("/"); 1394 if (pt < 0 && !noPath) 1395 name = "/" + name; 1396 if (pt < 0 || noPath) 1397 pt++; 1398 name = dataPath + name.substring(pt); 1399 } 1400 } 1401 Logger.info("FileManager substituting " + name0 + " --> " + name); 1402 oldFileNames.addLast("\"" + name0 + "\""); 1403 newFileNames.addLast("\1\"" + name + "\""); 1404 } 1405 return PT.replaceStrings(script, oldFileNames, newFileNames); 1406 } 1407 1408 //// CACHING //// 1409 1410 private Map<String, Object> cache = new Hashtable<String, Object>(); 1411 public Map<String, Object> pngjCache; 1412 public Map<String, byte[]> spardirCache; 1413 cachePut(String key, Object data)1414 void cachePut(String key, Object data) { 1415 key = fixDOSName(key); 1416 if (Logger.debugging) 1417 Logger.debug("cachePut " + key); 1418 if (data == null || "".equals(data)) { // J2S error -- cannot implement Int32Array.equals 1419 cache.remove(key); 1420 return; 1421 } 1422 cache.put(key, data); 1423 getCachedPngjBytes(key); 1424 } 1425 cacheGet(String key, boolean bytesOnly)1426 public Object cacheGet(String key, boolean bytesOnly) { 1427 key = fixDOSName(key); 1428 // in the case of JavaScript local file reader, 1429 // this will be a cached file, and the filename will not be known. 1430 int pt = key.indexOf("|"); 1431 if (pt >= 0 && !key.endsWith("##JmolSurfaceInfo##")) // check for PyMOL surface creation 1432 key = key.substring(0, pt); 1433 key = getFilePath(key, true, false); 1434 Object data = null; 1435 /** 1436 * @j2sNative 1437 * 1438 * (data = Jmol.Cache.get(key)) || (data = this.cache.get(key)); 1439 * 1440 */ 1441 { 1442 //if (Logger.debugging) 1443 //Logger.debug 1444 data = cache.get(key); 1445 if (data != null) 1446 Logger.info("cacheGet " + key); 1447 } 1448 return (bytesOnly && (data instanceof String) ? null : data); 1449 } 1450 cacheClear()1451 void cacheClear() { 1452 Logger.info("cache cleared"); 1453 cache.clear(); 1454 //String fileName = null; 1455 //fileName = fileName == null ? null : getCanonicalName(Rdr.getZipRoot(fileName)); 1456 if (pngjCache == null)// || fileName != null && !pngjCache.containsKey(fileName)) 1457 return; 1458 pngjCache = null; 1459 Logger.info("PNGJ cache cleared"); 1460 } 1461 cacheFileByNameAdd(String fileName, boolean isAdd)1462 public int cacheFileByNameAdd(String fileName, boolean isAdd) { 1463 if (fileName == null || !isAdd && fileName.equalsIgnoreCase("")) { 1464 cacheClear(); 1465 return -1; 1466 } 1467 Object data; 1468 if (isAdd) { 1469 fileName = JC.fixProtocol(vwr.resolveDatabaseFormat(fileName)); 1470 data = getFileAsBytes(fileName, null); 1471 if (data instanceof String) 1472 return 0; 1473 cachePut(fileName, data); 1474 } else { 1475 if (fileName.endsWith("*")) 1476 return AU.removeMapKeys(cache, fileName.substring(0, fileName.length() - 1)); 1477 data = cache.remove(fixDOSName(fileName)); 1478 } 1479 return (data == null ? 0 : data instanceof String ? ((String) data).length() 1480 : ((byte[]) data).length); 1481 } 1482 cacheList()1483 public Map<String, Integer> cacheList() { 1484 Map<String, Integer> map = new Hashtable<String, Integer>(); 1485 for (Map.Entry<String, Object> entry : cache.entrySet()) 1486 map.put(entry.getKey(), Integer 1487 .valueOf(AU.isAB(entry.getValue()) ? ((byte[]) entry 1488 .getValue()).length : entry.getValue().toString().length())); 1489 return map; 1490 } 1491 getCanonicalName(String pathName)1492 public String getCanonicalName(String pathName) { 1493 String[] names = getClassifiedName(pathName, true); 1494 return (names == null ? pathName : names[2]); 1495 } 1496 recachePngjBytes(String fileName, byte[] bytes)1497 public void recachePngjBytes(String fileName, byte[] bytes) { 1498 if (pngjCache == null || !pngjCache.containsKey(fileName)) 1499 return; 1500 pngjCache.put(fileName, bytes); 1501 Logger.info("PNGJ recaching " + fileName + " (" + bytes.length + ")"); 1502 } 1503 getPngjOrDroppedBytes(String fullName, String name)1504 private byte[] getPngjOrDroppedBytes(String fullName, String name) { 1505 byte[] bytes = getCachedPngjBytes(fullName); 1506 return (bytes == null ? (byte[]) cacheGet(name, true) : bytes); 1507 } 1508 getCachedPngjBytes(String pathName)1509 private byte[] getCachedPngjBytes(String pathName) { 1510 return ( 1511 pathName == null || pngjCache == null || pathName.indexOf(".png") < 0 ? null 1512 : getJzu().getCachedPngjBytes(this, pathName)); 1513 } 1514 1515 //// BytePoster interface 1516 1517 @Override postByteArray(String fileName, byte[] bytes)1518 public String postByteArray(String fileName, byte[] bytes) { 1519 // BytePoster interface - for javajs.util.OC (output channel) 1520 // in principle, could have sftp or ftp here 1521 // but sftp is not implemented 1522 if (fileName.startsWith("cache://")) { 1523 cachePut(fileName, bytes); 1524 return "OK " + bytes.length + "cached"; 1525 } 1526 Object ret = getBufferedInputStreamOrErrorMessageFromName(fileName, null, false, 1527 false, bytes, false, true); 1528 if (ret instanceof String) 1529 return (String) ret; 1530 try { 1531 ret = Rdr.getStreamAsBytes((BufferedInputStream) ret, null); 1532 } catch (IOException e) { 1533 try { 1534 ((BufferedInputStream) ret).close(); 1535 } catch (IOException e1) { 1536 // ignore 1537 } 1538 } 1539 return (ret == null ? "" : Rdr.fixUTF((byte[]) ret)); 1540 } 1541 1542 /** 1543 * Check to see if this is a Jmol WRITE file type that might be or have attached a ZIP collection . 1544 * 1545 * @param type the 1546 * @return true if PNG, PNGJ, JMOL, ZIP, or ZIPALL 1547 */ isJmolType(String type)1548 public static boolean isJmolType(String type) { 1549 return (type.equals("PNG") || type.equals("PNGJ") || type.equals("JMOL") || type.equals("ZIP") || type.equals("ZIPALL")); 1550 } 1551 1552 /** 1553 * Check to see if it is possible that this file has been embedded by Jmol 1554 * using JC.EMBEDDED_SCRIPT_TAG. This includes all Jmol types, JPEG images, and 1555 * export types POV, POVRAY, and IDTF 1556 * 1557 * @param type raw extension or file name 1558 * @return true if this file might contain an embedded script 1559 */ isEmbeddable(String type)1560 public static boolean isEmbeddable(String type) { 1561 int pt = type.lastIndexOf('.'); 1562 if (pt >= 0) 1563 type = type.substring(pt + 1); 1564 type = type.toUpperCase(); 1565 // note - not JPG64 here. Presumes that is just for image creation in JavaScript 1566 return (isJmolType(type) || PT.isOneOf(type, ";JPG;JPEG;POV;IDTF;")); 1567 } 1568 1569 /** 1570 * Get the specified SPT file of a Jmol zip collection or the embedded script 1571 * for any other file that is embeddable. 1572 * 1573 * @param fileName 1574 * @param allowCached 1575 * @param sptName 1576 * state.spt, movie.spt, or null 1577 * @return embedded state.spt, movie.spt, or a script embedded using 1578 * JC.EMBEDDED_SCRIPT_TAG, or "" if not found. 1579 */ getEmbeddedFileState(String fileName, boolean allowCached, String sptName)1580 public String getEmbeddedFileState(String fileName, boolean allowCached, String sptName) { 1581 if (!isEmbeddable(fileName)) 1582 return ""; 1583 String[] dir = getZipDirectory(fileName, false, allowCached); 1584 if (dir.length == 0) { 1585 String state = vwr.getFileAsString4(fileName, -1, false, true, false, "file"); 1586 return (state.indexOf(JC.EMBEDDED_SCRIPT_TAG) < 0 ? "" 1587 : getEmbeddedScript(state)); 1588 } 1589 for (int i = 0; i < dir.length; i++) 1590 if (dir[i].indexOf(sptName) >= 0) { 1591 String[] data = new String[] { fileName + "|" + dir[i], null }; 1592 getFileDataAsString(data, -1, false, false, false); 1593 return data[1]; 1594 } 1595 return ""; 1596 } 1597 1598 /** 1599 * Stip PDB::file://... from a file name 1600 * 1601 * @param fileName 1602 * @return stripped name 1603 */ stripTypePrefix(String fileName)1604 public static String stripTypePrefix(String fileName) { 1605 int pt = fileName.indexOf("::"); 1606 return (pt < 0 || pt >= 20 ? fileName : fileName.substring(pt + 2)); 1607 } 1608 1609 /** 1610 * Extract a Jmol script embedded using JC.EMBEDDED_SCRIPT_TAG. 1611 * 1612 * @param s 1613 * @return the embedded script or null 1614 */ getEmbeddedScript(String s)1615 public static String getEmbeddedScript(String s) { 1616 if (s == null) 1617 return s; 1618 int pt = s.indexOf(JC.EMBEDDED_SCRIPT_TAG); 1619 if (pt < 0) 1620 return s; 1621 int pt1 = s.lastIndexOf("/*", pt); 1622 int pt2 = s.indexOf((s.charAt(pt1 + 2) == '*' ? "*" : "") + "*/", 1623 pt); 1624 if (pt1 >= 0 && pt2 >= pt) 1625 s = s.substring( 1626 pt + JC.EMBEDDED_SCRIPT_TAG.length(), pt2) 1627 + "\n"; 1628 while ((pt1 = s.indexOf(JPEG_CONTINUE_STRING)) >= 0) 1629 s = s.substring(0, pt1) 1630 + s.substring(pt1 + JPEG_CONTINUE_STRING.length() + 4); 1631 if (Logger.debugging) 1632 Logger.debug(s); 1633 return s; 1634 } 1635 1636 } 1637