001package org.hl7.fhir.utilities.cache; 002 003/*- 004 * #%L 005 * org.hl7.fhir.utilities 006 * %% 007 * Copyright (C) 2014 - 2019 Health Level 7 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023 024import java.io.BufferedOutputStream; 025import java.io.ByteArrayInputStream; 026import java.io.ByteArrayOutputStream; 027import java.io.File; 028import java.io.FileInputStream; 029import java.io.FileNotFoundException; 030import java.io.IOException; 031import java.io.InputStream; 032import java.io.OutputStream; 033import java.nio.charset.Charset; 034import java.util.ArrayList; 035import java.util.Collections; 036import java.util.HashMap; 037import java.util.List; 038import java.util.Map; 039import java.util.Map.Entry; 040import java.util.Set; 041import java.util.zip.ZipEntry; 042import java.util.zip.ZipInputStream; 043 044import org.apache.commons.compress.archivers.tar.TarArchiveEntry; 045import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; 046import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; 047import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; 048import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; 049import org.apache.commons.io.FileUtils; 050import org.apache.commons.io.IOUtils; 051import org.hl7.fhir.exceptions.FHIRException; 052import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 053import org.hl7.fhir.utilities.IniFile; 054import org.hl7.fhir.utilities.TextFile; 055import org.hl7.fhir.utilities.Utilities; 056import org.hl7.fhir.utilities.cache.PackageCacheManager.PackageEntry; 057import org.hl7.fhir.utilities.cache.PackageGenerator.PackageType; 058import org.hl7.fhir.utilities.json.JSONUtil; 059import org.hl7.fhir.utilities.json.JsonTrackingParser; 060 061import com.google.common.base.Charsets; 062import com.google.gson.GsonBuilder; 063import com.google.gson.JsonArray; 064import com.google.gson.JsonElement; 065import com.google.gson.JsonObject; 066 067/** 068 * info and loader for a package 069 * 070 * Packages may exist on disk in the cache, or purely in memory when they are loaded on the fly 071 * 072 * Packages are contained in subfolders (see the package spec). The FHIR resources will be in "package" 073 * 074 * @author Grahame Grieve 075 * 076 */ 077public class NpmPackage { 078 079 public static boolean isValidName(String pid) { 080 return pid.matches("^[a-z][a-zA-Z0-9]*(\\.[a-z][a-zA-Z0-9]*)+$"); 081 } 082 083 public static boolean isValidVersion(String ver) { 084 return ver.matches("^[0-9]+\\.[0-9]+\\.[0-9]+$"); 085 } 086 087 public class NpmPackageFolder { 088 private String name; 089 private Map<String, List<String>> types = new HashMap<>(); 090 private Map<String, byte[]> content = new HashMap<String, byte[]>(); 091 private JsonObject index; 092 private File folder; 093 094 public NpmPackageFolder(String name) { 095 super(); 096 this.name = name; 097 } 098 099 public String getName() { 100 return name; 101 } 102 103 public void readIndex(JsonObject index) { 104 this.index = index; 105 for (JsonElement e : index.getAsJsonArray("files")) { 106 JsonObject file = (JsonObject) e; 107 String type = JSONUtil.str(file, "resourceType"); 108 String name = JSONUtil.str(file, "filename"); 109 if (!types.containsKey(type)) 110 types.put(type, new ArrayList<>()); 111 types.get(type).add(name); 112 } 113 } 114 115 public List<String> listFiles() { 116 List<String> res = new ArrayList<>(); 117 if (folder != null) { 118 for (File f : folder.listFiles()) { 119 if (!f.isDirectory() && !Utilities.existsInList(f.getName(), "package.json", ".index.json")) { 120 res.add(f.getName()); 121 } 122 } 123 } else { 124 for (String s : content.keySet()) { 125 if (!Utilities.existsInList(s, "package.json", ".index.json")) { 126 res.add(s); 127 } 128 } 129 } 130 Collections.sort(res); 131 return res; 132 } 133 134 public Map<String, byte[]> getContent() { 135 return content; 136 } 137 138 public byte[] fetchFile(String file) throws FileNotFoundException, IOException { 139 if (folder != null) { 140 File f = new File(Utilities.path(folder.getAbsolutePath(), file)); 141 if (f.exists()) { 142 return TextFile.fileToBytes(f); 143 } else { 144 return null; 145 } 146 } else { 147 return content.get(file); 148 } 149 } 150 151 public boolean hasFile(String file) throws IOException { 152 if (folder != null) { 153 return new File(Utilities.path(folder.getAbsolutePath(), file)).exists(); 154 } else { 155 return content.containsKey(file); 156 } 157 158 } 159 160 public String dump() { 161 return name + " ("+ (folder == null ? "null" : folder.toString())+") | "+Boolean.toString(index != null)+" | "+content.size()+" | "+types.size(); 162 } 163 164 } 165 166 private String path; 167 private JsonObject npm; 168 private Map<String, NpmPackageFolder> folders = new HashMap<>(); 169 170 private NpmPackage() { 171 172 } 173 // public NpmPackage(JsonObject npm, Map<String, byte[]> content, List<String> folders) { 174 // this.path = null; 175 // this.content = content; 176 // this.npm = npm; 177 // this.folders = folders; 178 // } 179 180 public static NpmPackage fromFolder(String path) throws IOException { 181 NpmPackage res = new NpmPackage(); 182 loadFiles(res, path, new File(path)); 183 res.checkIndexed(path); 184 return res; 185 } 186 187 public static void loadFiles(NpmPackage res, String path, File source, String... exemptions) throws FileNotFoundException, IOException { 188 res.npm = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(Utilities.path(path, "package", "package.json"))); 189 190 File dir = new File(path); 191 for (File f : dir.listFiles()) { 192 if (!Utilities.existsInList(f.getName(), ".git", ".svn") && !Utilities.existsInList(f.getName(), exemptions)) { 193 if (f.isDirectory()) { 194 String d = f.getName(); 195 if (!d.equals("package")) { 196 d = Utilities.path("package", d); 197 } 198 NpmPackageFolder folder = res.new NpmPackageFolder(d); 199 folder.folder = f; 200 res.folders.put(d, folder); 201 File ij = new File(Utilities.path(f.getAbsolutePath(), ".index.json")); 202 if (ij.exists()) { 203 try { 204 folder.readIndex(JsonTrackingParser.parseJson(ij)); 205 } catch (Exception e) { 206 throw new IOException("Error parsing "+ij.getAbsolutePath()+": "+e.getMessage(), e); 207 } 208 } 209 loadSubFolders(res, dir.getAbsolutePath(), f); 210 } else { 211 NpmPackageFolder folder = res.new NpmPackageFolder("package/$root"); 212 folder.folder = dir; 213 res.folders.put("package/$root", folder); 214 } 215 } 216 } 217 } 218 219 private static void loadSubFolders(NpmPackage res, String rootPath, File dir) throws IOException { 220 for (File f : dir.listFiles()) { 221 if (f.isDirectory()) { 222 String d = f.getAbsolutePath().substring(rootPath.length()+1); 223 if (!d.startsWith("package")) { 224 d = Utilities.path("package", d); 225 } 226 NpmPackageFolder folder = res.new NpmPackageFolder(d); 227 folder.folder = f; 228 res.folders.put(d, folder); 229 File ij = new File(Utilities.path(f.getAbsolutePath(), ".index.json")); 230 if (ij.exists()) { 231 try { 232 folder.readIndex(JsonTrackingParser.parseJson(ij)); 233 } catch (Exception e) { 234 throw new IOException("Error parsing "+ij.getAbsolutePath()+": "+e.getMessage(), e); 235 } 236 } 237 loadSubFolders(res, rootPath, f); 238 239 } 240 } 241 } 242 243 public static NpmPackage fromFolder(String folder, PackageType defType, String... exemptions) throws IOException { 244 NpmPackage res = new NpmPackage(); 245 loadFiles(res, folder, new File(folder), exemptions); 246 if (!res.folders.containsKey("package")) { 247 res.folders.put("package", res.new NpmPackageFolder("package")); 248 } 249 if (!res.folders.get("package").hasFile("package.json") && defType != null) { 250 TextFile.stringToFile("{ \"type\" : \""+defType.getCode()+"\"}", Utilities.path(res.folders.get("package").folder.getAbsolutePath(), "package.json")); 251 } 252 res.npm = (JsonObject) new com.google.gson.JsonParser().parse(new String(res.folders.get("package").fetchFile("package.json"))); 253 return res; 254 } 255 256 private static final int BUFFER_SIZE = 1024; 257 258 public static NpmPackage fromPackage(InputStream tgz) throws IOException { 259 return fromPackage(tgz, null, false); 260 } 261 262 public static NpmPackage fromPackage(InputStream tgz, String desc) throws IOException { 263 return fromPackage(tgz, desc, false); 264 } 265 266 public static NpmPackage fromPackage(InputStream tgz, String desc, boolean progress) throws IOException { 267 NpmPackage res = new NpmPackage(); 268 res.readStream(tgz, desc, progress); 269 return res; 270 } 271 272 public void readStream(InputStream tgz, String desc, boolean progress) throws IOException { 273 GzipCompressorInputStream gzipIn = new GzipCompressorInputStream(tgz); 274 try (TarArchiveInputStream tarIn = new TarArchiveInputStream(gzipIn)) { 275 TarArchiveEntry entry; 276 277 int i = 0; 278 int c = 12; 279 while ((entry = (TarArchiveEntry) tarIn.getNextEntry()) != null) { 280 i++; 281 String n = entry.getName(); 282 if (entry.isDirectory()) { 283 String dir = n.substring(0, n.length()-1); 284 if (dir.startsWith("package/")) { 285 dir = dir.substring(8); 286 } 287 folders.put(dir, new NpmPackageFolder(dir)); 288 } else { 289 int count; 290 byte data[] = new byte[BUFFER_SIZE]; 291 ByteArrayOutputStream fos = new ByteArrayOutputStream(); 292 try (BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) { 293 while ((count = tarIn.read(data, 0, BUFFER_SIZE)) != -1) { 294 dest.write(data, 0, count); 295 } 296 } 297 fos.close(); 298 loadFile(n, fos.toByteArray()); 299 } 300 if (progress && i % 50 == 0) { 301 c++; 302 System.out.print("."); 303 if (c == 120) { 304 System.out.println(""); 305 System.out.print(" "); 306 c = 2; 307 } 308 } 309 } 310 } 311 try { 312 npm = JsonTrackingParser.parseJson(folders.get("package").fetchFile("package.json")); 313 } catch (Exception e) { 314 throw new IOException("Error parsing "+(desc == null ? "" : desc+"#")+"package/package.json: "+e.getMessage(), e); 315 } 316 checkIndexed(desc); 317 } 318 319 public void loadFile(String n, byte[] data) throws IOException { 320 String dir = n.contains("/") ? n.substring(0, n.lastIndexOf("/")) : "$root"; 321 if (dir.startsWith("package/")) { 322 dir = dir.substring(8); 323 } 324 n = n.substring(n.lastIndexOf("/")+1); 325 NpmPackageFolder index = folders.get(dir); 326 if (index == null) { 327 index = new NpmPackageFolder(dir); 328 folders.put(dir, index); 329 } 330 index.content.put(n, data); 331 } 332 333 private void checkIndexed(String desc) throws IOException { 334 for (NpmPackageFolder folder : folders.values()) { 335 if (folder.index == null) { 336 NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder(); 337 indexer.start(); 338 for (String n : folder.listFiles()) { 339 indexer.seeFile(n, folder.fetchFile(n)); 340 } 341 String json = indexer.build(); 342 try { 343 folder.readIndex(JsonTrackingParser.parseJson(json)); 344 } catch (Exception e) { 345 TextFile.stringToFile(json, Utilities.path("[tmp]", ".index.json")); 346 throw new IOException("Error parsing "+(desc == null ? "" : desc+"#")+"package/"+folder.name+"/.index.json: "+e.getMessage(), e); 347 } 348 } 349 } 350 } 351 352 353 public static NpmPackage fromZip(InputStream stream, boolean dropRootFolder, String desc) throws IOException { 354 NpmPackage res = new NpmPackage(); 355 ZipInputStream zip = new ZipInputStream(stream); 356 ZipEntry ze; 357 while ((ze = zip.getNextEntry()) != null) { 358 int size; 359 byte[] buffer = new byte[2048]; 360 361 ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 362 BufferedOutputStream bos = new BufferedOutputStream(bytes, buffer.length); 363 364 while ((size = zip.read(buffer, 0, buffer.length)) != -1) { 365 bos.write(buffer, 0, size); 366 } 367 bos.flush(); 368 bos.close(); 369 if (bytes.size() > 0) { 370 if (dropRootFolder) { 371 res.loadFile(ze.getName().substring(ze.getName().indexOf("/")+1), bytes.toByteArray()); 372 } else { 373 res.loadFile(ze.getName(), bytes.toByteArray()); 374 } 375 } 376 zip.closeEntry(); 377 } 378 zip.close(); 379 try { 380 res.npm = JsonTrackingParser.parseJson(res.folders.get("package").fetchFile("package.json")); 381 } catch (Exception e) { 382 throw new IOException("Error parsing "+(desc == null ? "" : desc+"#")+"package/package.json: "+e.getMessage(), e); 383 } 384 res.checkIndexed(desc); 385 return res; 386 } 387 388 389 /** 390 * Accessing the contents of the package - get a list of files in a subfolder of the package 391 * 392 * @param folder 393 * @return 394 * @throws IOException 395 */ 396 public List<String> list(String folder) throws IOException { 397 List<String> res = new ArrayList<String>(); 398 if (folders.containsKey(folder)) { 399 res.addAll(folders.get(folder).listFiles()); 400 } else if (folders.containsKey(Utilities.path("package", folder))) { 401 res.addAll(folders.get(Utilities.path("package", folder)).listFiles()); 402 } 403 return res; 404 } 405 406 public List<String> listResources(String... types) throws IOException { 407 List<String> res = new ArrayList<String>(); 408 NpmPackageFolder folder = folders.get("package"); 409 for (String s : types) { 410 if (folder.types.containsKey(s)) 411 res.addAll(folder.types.get(s)); 412 } 413 Collections.sort(res); 414 return res; 415 } 416 417 /** 418 * use the name from listResources() 419 * 420 * @param id 421 * @return 422 * @throws IOException 423 */ 424 public InputStream loadResource(String file) throws IOException { 425 NpmPackageFolder folder = folders.get("package"); 426 return new ByteArrayInputStream(folder.fetchFile(file)); 427 } 428 429 /** 430 * get a stream that contains the contents of one of the files in a folder 431 * 432 * @param folder 433 * @param file 434 * @return 435 * @throws IOException 436 */ 437 public InputStream load(String folder, String file) throws IOException { 438 NpmPackageFolder f = folders.get(folder); 439 if (f == null) { 440 f = folders.get(Utilities.path("package", folder)); 441 } 442 if (f != null && f.hasFile(file)) { 443 return new ByteArrayInputStream(f.fetchFile(file)); 444 } else { 445 throw new IOException("Unable to find the file "+folder+"/"+file+" in the package "+name()); 446 } 447 } 448 449 public boolean hasFile(String folder, String file) throws IOException { 450 NpmPackageFolder f = folders.get(folder); 451 return f != null && f.hasFile(file); 452 } 453 454 455 /** 456 * Handle to the package json file 457 * 458 * @return 459 */ 460 public JsonObject getNpm() { 461 return npm; 462 } 463 464 /** 465 * convenience method for getting the package name 466 * @return 467 */ 468 public String name() { 469 return JSONUtil.str(npm, "name"); 470 } 471 472 public String date() { 473 return JSONUtil.str(npm, "date"); 474 } 475 476 public String canonical() { 477 return JSONUtil.str(npm, "canonical"); 478 } 479 480 /** 481 * convenience method for getting the package version 482 * @return 483 */ 484 public String version() { 485 return JSONUtil.str(npm, "version"); 486 } 487 488 /** 489 * convenience method for getting the package fhir version 490 * @return 491 */ 492 public String fhirVersion() { 493 if ("hl7.fhir.core".equals(JSONUtil.str(npm, "name"))) 494 return JSONUtil.str(npm, "version"); 495 else if (JSONUtil.str(npm, "name").startsWith("hl7.fhir.r2.") || JSONUtil.str(npm, "name").startsWith("hl7.fhir.r2b.") || JSONUtil.str(npm, "name").startsWith("hl7.fhir.r3.") || JSONUtil.str(npm, "name").startsWith("hl7.fhir.r4.") || JSONUtil.str(npm, "name").startsWith("hl7.fhir.r5.")) 496 return JSONUtil.str(npm, "version"); 497 else { 498 JsonObject dep = npm.getAsJsonObject("dependencies"); 499 if (dep != null) { 500 for (Entry<String, JsonElement> e : dep.entrySet()) { 501 if (Utilities.existsInList(e.getKey(), "hl7.fhir.r2.core", "hl7.fhir.r2b.core", "hl7.fhir.r3.core", "hl7.fhir.r4.core")) 502 return e.getValue().getAsString(); 503 if (Utilities.existsInList(e.getKey(), "hl7.fhir.core")) // while all packages are updated 504 return e.getValue().getAsString(); 505 } 506 } 507 if (npm.has("fhirVersions")) { 508 return npm.getAsJsonArray("fhirVersions").get(0).getAsString(); 509 } 510 if (dep != null) { 511 // legacy simplifier support: 512 if (dep.has("simplifier.core.r4")) 513 return "4.0"; 514 if (dep.has("simplifier.core.r3")) 515 return "3.0"; 516 if (dep.has("simplifier.core.r2")) 517 return "2.0"; 518 } 519 throw new FHIRException("no core dependency or FHIR Version found in the Package definition"); 520 } 521 } 522 523 public String summary() { 524 if (path != null) 525 return path; 526 else 527 return "memory"; 528 } 529 530 public boolean isType(PackageType template) { 531 return template.getCode().equals(type()); 532 } 533 534 public String type() { 535 return JSONUtil.str(npm, "type"); 536 } 537 538 public String description() { 539 return JSONUtil.str(npm, "description"); 540 } 541 542 public String getPath() { 543 return path; 544 } 545 546 public List<String> dependencies() { 547 List<String> res = new ArrayList<>(); 548 if (npm.has("dependencies")) { 549 for (Entry<String, JsonElement> e : npm.getAsJsonObject("dependencies").entrySet()) { 550 res.add(e.getKey()+"#"+e.getValue().getAsString()); 551 } 552 } 553 return res; 554 } 555 556 public String homepage() { 557 return JSONUtil.str(npm, "homepage"); 558 } 559 560 public String url() { 561 return JSONUtil.str(npm, "url"); 562 } 563 564 565 public String title() { 566 return JSONUtil.str(npm, "title"); 567 } 568 569 public String toolsVersion() { 570 return JSONUtil.str(npm, "tools-version"); 571 } 572 573 public String license() { 574 return JSONUtil.str(npm, "license"); 575 } 576 577 // /** 578 // * only for use by the package manager itself 579 // * 580 // * @param path 581 // */ 582 // public void setPath(String path) { 583 // this.path = path; 584 // } 585 586 public String getWebLocation() { 587 if (npm.has("url")) { 588 return npm.get("url").getAsString(); 589 } else { 590 return JSONUtil.str(npm, "canonical"); 591 } 592 } 593 594 public InputStream loadResource(String type, String id) throws IOException { 595 NpmPackageFolder f = folders.get("package"); 596 JsonArray files = f.index.getAsJsonArray("files"); 597 for (JsonElement e : files) { 598 JsonObject i = (JsonObject) e; 599 if (type.equals(JSONUtil.str(i, "resourceType")) && id.equals(JSONUtil.str(i, "id"))) { 600 return load("package", JSONUtil.str(i, "filename")); 601 } 602 } 603 return null; 604 } 605 606 public InputStream loadExampleResource(String type, String id) throws IOException { 607 NpmPackageFolder f = folders.get("example"); 608 if (f != null) { 609 JsonArray files = f.index.getAsJsonArray("files"); 610 for (JsonElement e : files) { 611 JsonObject i = (JsonObject) e; 612 if (type.equals(JSONUtil.str(i, "resourceType")) && id.equals(JSONUtil.str(i, "id"))) { 613 return load("example", JSONUtil.str(i, "filename")); 614 } 615 } 616 } 617 return null; 618 } 619 620 /** special case when playing around inside the package **/ 621 public Map<String, NpmPackageFolder> getFolders() { 622 return folders; 623 } 624 625 public void save(OutputStream stream) throws IOException { 626 TarArchiveOutputStream tar; 627 ByteArrayOutputStream OutputStream; 628 BufferedOutputStream bufferedOutputStream; 629 GzipCompressorOutputStream gzipOutputStream; 630 631 OutputStream = new ByteArrayOutputStream(); 632 bufferedOutputStream = new BufferedOutputStream(OutputStream); 633 gzipOutputStream = new GzipCompressorOutputStream(bufferedOutputStream); 634 tar = new TarArchiveOutputStream(gzipOutputStream); 635 636 637 for (NpmPackageFolder folder : folders.values()) { 638 String n = folder.name; 639 if (!"package".equals(n)) { 640 n = "package/"+n; 641 } 642 NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder(); 643 indexer.start(); 644 for (String s : folder.content.keySet()) { 645 byte[] b = folder.content.get(s); 646 String name = n+"/"+s; 647 indexer.seeFile(s, b); 648 if (!s.equals(".index.json") && !s.equals("package.json")) { 649 TarArchiveEntry entry = new TarArchiveEntry(name); 650 entry.setSize(b.length); 651 tar.putArchiveEntry(entry); 652 tar.write(b); 653 tar.closeArchiveEntry(); 654 } 655 } 656 byte[] cnt = indexer.build().getBytes(Charset.forName("UTF-8")); 657 TarArchiveEntry entry = new TarArchiveEntry(n+"/.index.json"); 658 entry.setSize(cnt.length); 659 tar.putArchiveEntry(entry); 660 tar.write(cnt); 661 tar.closeArchiveEntry(); 662 } 663 byte[] cnt = TextFile.stringToBytes(new GsonBuilder().setPrettyPrinting().create().toJson(npm), false); 664 TarArchiveEntry entry = new TarArchiveEntry("package/package.json"); 665 entry.setSize(cnt.length); 666 tar.putArchiveEntry(entry); 667 tar.write(cnt); 668 tar.closeArchiveEntry(); 669 670 tar.finish(); 671 tar.close(); 672 gzipOutputStream.close(); 673 bufferedOutputStream.close(); 674 OutputStream.close(); 675 byte[] b = OutputStream.toByteArray(); 676 stream.write(b); 677 } 678 679 680 public Map<String, List<String>> getTypes() { 681 return folders.get("package").types; 682 } 683 684 public String fhirVersionList() { 685 if (npm.has("fhirVersions")) { 686 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 687 for (JsonElement n : npm.getAsJsonArray("fhirVersions")) { 688 b.append(n.getAsString()); 689 } 690 return b.toString(); 691 } else 692 return ""; 693 } 694 695 public String dependencySummary() { 696 if (npm.has("dependencies")) { 697 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 698 for (Entry<String, JsonElement> e : npm.getAsJsonObject("dependencies").entrySet()) { 699 b.append(e.getKey()+"#"+e.getValue().getAsString()); 700 } 701 return b.toString(); 702 } else 703 return ""; 704 } 705 706 public void unPack(String dir) throws IOException { 707 unPack (dir, false); 708 } 709 710 public void unPackWithAppend(String dir) throws IOException { 711 unPack (dir, true); 712 } 713 714 public void unPack(String dir, boolean withAppend) throws IOException { 715 for (NpmPackageFolder folder : folders.values()) { 716 String dn = folder.getName(); 717 if (!dn.equals("package") && (dn.startsWith("package/") || dn.startsWith("package\\"))) { 718 dn = dn.substring(8); 719 } 720 if (dn.equals("$root")) { 721 dn = dir; 722 } else { 723 dn = Utilities.path(dir, dn); 724 } 725 Utilities.createDirectory(dn); 726 for (String s : folder.listFiles()) { 727 String fn = Utilities.path(dn, s); 728 File f = new File(fn); 729 if (withAppend && f.getName().startsWith("_append.")) { 730 String appendFn = Utilities.path(dn, s.substring(8)); 731 if (new File(appendFn).exists()) 732 TextFile.appendBytesToFile(folder.fetchFile(s), appendFn); 733 else 734 TextFile.bytesToFile(folder.fetchFile(s), appendFn); 735 } else 736 TextFile.bytesToFile(folder.fetchFile(s), fn); 737 } 738// if (path != null) 739// FileUtils.copyDirectory(new File(path), new File(dir)); 740 } 741 } 742 743 public void debugDump(String purpose) { 744// System.out.println("Debug Dump of Package for '"+purpose+"'. Path = "+path); 745// System.out.println(" npm = "+name()+"#"+version()+", canonical = "+canonical()); 746// System.out.println(" folders = "+folders.size()); 747// for (String s : sorted(folders.keySet())) { 748// NpmPackageFolder folder = folders.get(s); 749// System.out.println(" "+folder.dump()); 750// } 751 } 752 753 private List<String> sorted(Set<String> keys) { 754 List<String> res = new ArrayList<String>(); 755 res.addAll(keys); 756 Collections.sort(res); 757 return res ; 758 } 759} 760