/*
 * Copyright 2014-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package com.facebook.buck.java;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;

@XmlRootElement(name = "fatjar")
@XmlAccessorType(XmlAccessType.FIELD)
public class FatJar {

  public static final String FAT_JAR_INFO_RESOURCE = "fat_jar_info.dat";

  /**
   * The resource name for the real JAR.
   */
  private String innerJar;

  /**
   * The map of system-specific shared library names to their corresponding resource names.
   */
  private Map<String, String> nativeLibraries;

  // Required for XML deserialization.
  protected FatJar() {}

  public FatJar(String innerJar, Map<String, String> nativeLibraries) {
    this.innerJar = innerJar;
    this.nativeLibraries = nativeLibraries;
  }

  /**
   * @return the {@link FatJar} object deserialized from the resource name via {@code loader}.
   */
  public static FatJar load(ClassLoader loader) throws Exception {
    try (InputStream inputStream = loader.getResourceAsStream(FAT_JAR_INFO_RESOURCE);
         BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) {
      XMLEventReader xmlEventReader =
          XMLInputFactory.newFactory().createXMLEventReader(bufferedInputStream);
      JAXBContext context = JAXBContext.newInstance(FatJar.class);
      Unmarshaller unmarshaller = context.createUnmarshaller();
      JAXBElement<FatJar> jaxbElementA = unmarshaller.unmarshal(xmlEventReader, FatJar.class);
      return jaxbElementA.getValue();
    }
  }

  /**
   * Serialize this instance as XML to {@code outputStream}.
   */
  public void store(OutputStream outputStream) throws Exception {
    JAXBContext context = JAXBContext.newInstance(FatJar.class);
    JAXBElement<FatJar> element = new JAXBElement<>(new QName("fatjar"), FatJar.class, this);
    Marshaller marshaller = context.createMarshaller();
    marshaller.marshal(element, outputStream);
  }

  public void unpackNativeLibrariesInto(ClassLoader loader, Path destination) throws IOException {
    for (Map.Entry<String, String> entry : nativeLibraries.entrySet()) {
      try (InputStream input = loader.getResourceAsStream(entry.getValue());
           BufferedInputStream bufferedInput = new BufferedInputStream(input)) {
        Files.copy(bufferedInput, destination.resolve(entry.getKey()));
      }
    }
  }

  public void unpackJarTo(ClassLoader loader, Path destination) throws IOException {
    try (InputStream input = loader.getResourceAsStream(innerJar);
         BufferedInputStream bufferedInput = new BufferedInputStream(input)) {
      Files.copy(bufferedInput, destination);
    }
  }

}

@@ -1327,16 +1327,543 @@
 FatJar %7B
+%0A  /**%0A   * Since FatJar is going to be embedded in many targets, it cannot have external dependencies, but%0A   * we'd like to have %7B@link javax.annotation.Nullable%7D and%0A   * %7B@link com.google.common.base.Preconditions#checkNotNull%7D anyway, so we define these here.%0A   */%0A  @interface Nullable %7B%7D%0A  private static class Preconditions %7B%0A    private Preconditions() %7B%7D%0A%0A    public static %3CT%3E T checkNotNull(@Nullable T value) %7B%0A      if (value == null) %7B%0A        throw new RuntimeException();%0A      %7D%0A      return value;%0A    %7D%0A  %7D
 %0A%0A  publ
@@ -1975,32 +1975,44 @@
 real JAR.%0A   */%0A
+  @Nullable%0A
   private String
@@ -2121,22 +2121,34 @@
  names.%0A
-
    */%0A
+  @Nullable%0A
   privat
@@ -3696,16 +3696,43 @@
 entry : 
+Preconditions.checkNotNull(
 nativeLi
@@ -3738,16 +3738,17 @@
 ibraries
+)
 .entrySe
@@ -4144,16 +4144,43 @@
 sStream(
+Preconditions.checkNotNull(
 innerJar
@@ -4180,16 +4180,17 @@
 nnerJar)
+)
 ;%0A      

Add inlined nullability checks to FatJar.