// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.jps.javac;

import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.BinaryContent;

import javax.tools.JavaFileManager;
import java.io.*;
import java.net.URI;
import java.util.Iterator;

/**
 * @author Eugene Zhuravlev
 */
public final class OutputFileObject extends JpsFileObject {
  @Nullable
  private final JpsJavacFileManager.Context myContext;
  @Nullable
  private final File myOutputRoot;
  private final String myRelativePath;
  private final File myFile;
  @Nullable
  private final String myClassName;
  private final Iterable<URI> mySources;
  private volatile BinaryContent myContent;
  private final String myEncodingName;
  private final boolean myIsGenerated;

  public OutputFileObject(@Nullable JpsJavacFileManager.Context context,
                          @Nullable File outputRoot,
                          String relativePath,
                          @NotNull File file,
                          @NotNull Kind kind,
                          @Nullable String className,
                          @NotNull final Iterable<URI> sources,
                          @Nullable final String encodingName,
                          @Nullable BinaryContent content,
                          final JavaFileManager.Location location,
                          boolean isFromGeneratedSource) {
    super(FileUtilRt.fileToUri(file), kind, location);
    myContext = context;
    mySources = sources;
    myContent = content;
    myOutputRoot = outputRoot;
    myRelativePath = relativePath;
    myFile = file;
    myClassName = className != null? className.replace('/', '.') : null;
    myEncodingName = encodingName;
    myIsGenerated = isFromGeneratedSource;
  }

  @Nullable
  public File getOutputRoot() {
    return myOutputRoot;
  }

  public String getRelativePath() {
    return myRelativePath;
  }

  @NotNull
  public File getFile() {
    return myFile;
  }

  @Nullable
  public String getClassName() {
    return myClassName;
  }

  public boolean isGenerated() {
    return myIsGenerated;
  }

  /**
   * @deprecated In general, an output object may be generated from several source files. Use {@link OutputFileObject#getSourceFiles()} method instead.
   */
  @Deprecated
  @Nullable
  public File getSourceFile() {
    final Iterator<File> it = getSourceFiles().iterator();
    return it.hasNext()? it.next() : null;
  }

  @NotNull
  public Iterable<File> getSourceFiles() {
    return Iterators.filter(Iterators.map(getSourceUris(), new Function<URI, File>() {
      @Override
      public File fun(URI uri) {
        return "file".equalsIgnoreCase(uri.getScheme())? new File(uri) : null;
      }
    }), Iterators.<File>notNullFilter());
  }

  @NotNull
  public Iterable<URI> getSourceUris() {
    return mySources;
  }

  @Override
  @Nullable
  protected String inferBinaryName(Iterable<? extends File> path, boolean caseSensitiveFS) {
    return null; // this will cause FileManager to delegate to JVM implementation
  }

  @Override
  public ByteArrayOutputStream openOutputStream() {
    return new ByteArrayOutputStream() {
      @Override
      public void close() throws IOException {
        try {
          super.close();
        }
        finally {
          myContent = new BinaryContent(buf, 0, size());
          if (myContext != null) {
            myContext.consumeOutputFile(OutputFileObject.this);
          }
        }
      }
    };
  }

  @Override
  public InputStream openInputStream() throws IOException {
    final BinaryContent bytes = myContent;
    if (bytes != null) {
      return new ByteArrayInputStream(bytes.getBuffer(), bytes.getOffset(), bytes.getLength());
    }
    return new BufferedInputStream(new FileInputStream(myFile));
  }

  @Override
  public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
    final BinaryContent content = myContent;
    final String encoding = myEncodingName;
    if (content != null) {
      return encoding == null ?
             new String(content.getBuffer(), content.getOffset(), content.getLength()) :
             new String(content.getBuffer(), content.getOffset(), content.getLength(), encoding);
    }
    return loadCharContent(myFile, encoding);
  }

  @Override
  public Writer openWriter() throws IOException {
    final String encoding = myEncodingName;
    return encoding != null? new OutputStreamWriter(openOutputStream(), encoding) : super.openWriter();
  }

  @Nullable
  public BinaryContent getContent() {
    return myContent;
  }

  public void updateContent(@NotNull byte[] updatedContent) {
    myContent = new BinaryContent(updatedContent, 0, updatedContent.length);
  }

}

@@ -3362,24 +3362,177 @@
 tStream() %7B%0A
+      private boolean isClosed = false;%0A%0A      private synchronized boolean markClosed() %7B%0A        return !isClosed && (isClosed = true);%0A      %7D%0A      %0A
       @Overr
@@ -3578,24 +3578,54 @@
 Exception %7B%0A
+        if (markClosed()) %7B%0A  
         try 
@@ -3632,24 +3632,26 @@
 %7B%0A          
+  
 super.close(
@@ -3649,24 +3649,26 @@
 er.close();%0A
+  
         %7D%0A  
@@ -3669,24 +3669,26 @@
   %7D%0A        
+  
 finally %7B%0A  
@@ -3691,24 +3691,26 @@
 %7B%0A          
+ 
+ 
 myContent = 
@@ -3740,24 +3740,26 @@
 0, size());%0A
+  
           if
@@ -3789,24 +3789,26 @@
             
+  
 myContext.co
@@ -3847,16 +3847,30 @@
 .this);%0A
+            %7D%0A
         

fix lombok-ecj stream double close (IDEA-254725)