// Copyright 2000-2018 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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.tools.JavaFileManager;
import javax.tools.SimpleJavaFileObject;
import java.io.*;
import java.net.URI;
import java.nio.CharBuffer;

/**
 * @author Eugene Zhuravlev
 * Date: 16-Oct-18
 */
public abstract class JpsFileObject extends SimpleJavaFileObject {
  private static final Kind[] ourAvailableKinds = Kind.values();
  private final JavaFileManager.Location myLocation;

  public JpsFileObject(URI uri, Kind kind, @Nullable JavaFileManager.Location location) {
    super(uri, kind);
    myLocation = location;
  }

  @Nullable
  public JavaFileManager.Location getLocation() {
    return myLocation;
  }

  protected static Kind findKind(String name) {
    for (Kind kind : ourAvailableKinds) {
      if (kind != Kind.OTHER && name.regionMatches(true, name.length() - kind.extension.length(), kind.extension, 0, kind.extension.length())) {
        return kind;
      }
    }
    return Kind.OTHER;
  }

  /**
   * @param path
   * @param caseSensitiveFS
   * @return null means the file manager should delegate to base implementation
   */
  @Nullable
  protected abstract String inferBinaryName(Iterable<? extends File> path, boolean caseSensitiveFS);

  @Override
  public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
    final CharSequence content = getCharContent(ignoreEncodingErrors);
    if (content == null) {
      throw new UnsupportedOperationException();
    }
    if (content instanceof CharBuffer) {
      final CharBuffer buffer = (CharBuffer)content;
      if (buffer.hasArray()) {
        return new CharArrayReader(buffer.array(), buffer.arrayOffset(), buffer.length());
      }
    }
    return new StringReader(content.toString());
  }

  @SuppressWarnings("Duplicates")
  @NotNull
  protected static CharSequence loadCharContent(@NotNull File file, @Nullable String encoding) throws IOException {
    // FileUtil.loadText clones char array if length mismatch
    final FileInputStream fileStream = new FileInputStream(file);
    final InputStream stream = new BufferedInputStream(fileStream);
    try {
      final Reader reader = encoding == null ? new InputStreamReader(stream) : new InputStreamReader(stream, encoding);
      try {
        // channel allows to avoid extra call to get file size because fd is reused, see Files.readAllBytes
        char[] chars = new char[(int)fileStream.getChannel().size()];
        int count = 0;
        while (count < chars.length) {
          int n = reader.read(chars, count, chars.length - count);
          if (n <= 0) {
            break;
          }
          count += n;
        }
        return CharBuffer.wrap(chars, 0, count);
      }
      finally {
        reader.close();
      }
    }
    finally {
      stream.close();
    }
  }

  @Override
  public int hashCode() {
    return toUri().hashCode();
  }

  @Override
  public boolean equals(Object obj) {
    // todo: check if this is fast enough to rely on URI.equals() here
    return this == obj || (obj instanceof JpsFileObject && toUri().equals(((JpsFileObject)obj).toUri()));
  }
}

@@ -2177,21 +2177,57 @@
 tch%0A    
-final
+Closeable toClose = null;%0A    try %7B%0A     
  FileInp
@@ -2283,34 +2283,206 @@
     
-final InputStream stream =
+  toClose = fileStream;%0A      final int size = (int)fileStream.getChannel().size();%0A      InputStream stream = size %3E 0? new BufferedInputStream(fileStream, Math.min(size, 512 * 1024 /* 512 kb*/)) :
  new
@@ -2515,25 +2515,39 @@
 tream);%0A
+  
     t
-ry %7B
+oClose = stream;
 %0A      f
@@ -2662,31 +2662,41 @@
 ng);%0A      t
-ry %7B%0A  
+oClose = reader;%0A
       // cha
@@ -2789,18 +2789,16 @@
 llBytes%0A
-  
       ch
@@ -2823,48 +2823,15 @@
 har%5B
-(int)fileStream.getChannel().
 size
-()
 %5D;%0A
-  
     
@@ -2847,18 +2847,16 @@
 nt = 0;%0A
-  
       wh
@@ -2888,26 +2888,24 @@
 ) %7B%0A        
-  
 int n = read
@@ -2953,26 +2953,24 @@
 t);%0A        
-  
 if (n %3C= 0) 
@@ -2967,26 +2967,24 @@
  (n %3C= 0) %7B%0A
-  
           br
@@ -2992,30 +2992,26 @@
 ak;%0A        
-  %7D%0A  
+%7D%0A
         coun
@@ -3020,30 +3020,26 @@
 += n;%0A      
-  
 %7D%0A
-  
       return
@@ -3073,30 +3073,26 @@
 count);%0A    
-  %7D%0A  
+%7D%0A
     finally 
@@ -3103,51 +3103,28 @@
     
-  reader.close();%0A      %7D%0A    %7D%0A    finally
+if (toClose != null)
  %7B%0A 
@@ -3128,22 +3128,25 @@
 %7B%0A      
-stream
+  toClose
 .close()
@@ -3143,24 +3143,32 @@
 se.close();%0A
+      %7D%0A
     %7D%0A  %7D%0A%0A 

add adaptive buffering