/*
 * Copyright 2016 the original author or authors.
 *
 * 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 org.gradle.integtests.fixtures.executer;

import net.jcip.annotations.ThreadSafe;
import org.apache.commons.io.output.CloseShieldOutputStream;
import org.apache.commons.io.output.TeeOutputStream;
import org.gradle.internal.io.StreamByteBuffer;

import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@ThreadSafe
class OutputCapturer {
    private final StreamByteBuffer buffer;
    private final OutputStream outputStream;
    private final StringBuilder outputStringBuilder = new StringBuilder();
    private final String outputEncoding;
    private String cachedOutputString;
    private final Lock lock = new ReentrantLock();

    public OutputCapturer(OutputStream standardStream, String outputEncoding) {
        this.buffer = new StreamByteBuffer();
        this.outputStream = new CloseShieldOutputStream(new TeeOutputStream(standardStream, new LockingOutputStream(buffer.getOutputStream(), lock)));
        this.outputEncoding = outputEncoding;
    }

    public OutputStream getOutputStream() {
        return outputStream;
    }

    public String getOutputAsString() {
        lock.lock();
        try {
            if (cachedOutputString == null || buffer.totalBytesUnread() > 0) {
                outputStringBuilder.append(buffer.readAsString(outputEncoding));
                cachedOutputString = outputStringBuilder.toString();
            }
            return cachedOutputString;
        } finally {
            lock.unlock();
        }
    }

    public void reset() {
        lock.lock();
        try {
            buffer.clear();
            outputStringBuilder.setLength(0);
            cachedOutputString = null;
        } finally {
            lock.unlock();
        }
    }

    private static class LockingOutputStream extends OutputStream {
        private final OutputStream delegate;
        private final Lock lock;

        LockingOutputStream(OutputStream delegate, Lock lock) {
            this.delegate = delegate;
            this.lock = lock;
        }

        @Override
        public void write(int b) throws IOException {
            lock.lock();
            try {
                delegate.write(b);
            } finally {
                lock.unlock();
            }
        }

        @Override
        public void write(byte[] b) throws IOException {
            lock.lock();
            try {
                delegate.write(b);
            } finally {
                lock.unlock();
            }
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            lock.lock();
            try {
                delegate.write(b, off, len);
            } finally {
                lock.unlock();
            }
        }

        @Override
        public void flush() throws IOException {
            lock.lock();
            try {
                delegate.flush();
            } finally {
                lock.unlock();
            }
        }

        @Override
        public void close() throws IOException {
            lock.lock();
            try {
                delegate.close();
            } finally {
                lock.unlock();
            }
        }
    }
}

@@ -664,48 +664,8 @@
 r;%0A%0A
-import net.jcip.annotations.ThreadSafe;%0A
 impo
@@ -827,36 +827,8 @@
 r;%0A%0A
-import java.io.IOException;%0A
 impo
@@ -856,109 +856,8 @@
 am;%0A
-import java.util.concurrent.locks.Lock;%0Aimport java.util.concurrent.locks.ReentrantLock;%0A%0A@ThreadSafe
 %0Acla
@@ -1122,59 +1122,8 @@
 ing;
-%0A    private final Lock lock = new ReentrantLock();
 %0A%0A  
@@ -1342,32 +1342,8 @@
 am, 
-new LockingOutputStream(
 buff
@@ -1366,15 +1366,8 @@
 am()
-, lock)
 ));%0A
@@ -1543,47 +1543,8 @@
 ) %7B%0A
-        lock.lock();%0A        try %7B%0A    
     
@@ -1618,36 +1618,32 @@
 ) %7B%0A            
- 
-   
 outputStringBuil
@@ -1695,36 +1695,32 @@
 ));%0A            
-    
 cachedOutputStri
@@ -1768,24 +1768,18 @@
     
-    %7D%0A  
+%7D%0A
         
   re
@@ -1770,26 +1770,24 @@
   %7D%0A        
-  
 return cache
@@ -1809,133 +1809,37 @@
     
-    %7D finally %7B%0A            lock.unlock();%0A        %7D%0A    %7D%0A%0A    public void reset() %7B%0A        lock.lock();%0A        try %7B%0A    
+%7D%0A%0A    public void reset() %7B%0A
     
@@ -1854,28 +1854,24 @@
 er.clear();%0A
-    
         outp
@@ -1900,20 +1900,16 @@
 gth(0);%0A
-    
         
@@ -1939,1533 +1939,8 @@
 ll;%0A
-        %7D finally %7B%0A            lock.unlock();%0A        %7D%0A    %7D%0A%0A    private static class LockingOutputStream extends OutputStream %7B%0A        private final OutputStream delegate;%0A        private final Lock lock;%0A%0A        LockingOutputStream(OutputStream delegate, Lock lock) %7B%0A            this.delegate = delegate;%0A            this.lock = lock;%0A        %7D%0A%0A        @Override%0A        public void write(int b) throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.write(b);%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A%0A        @Override%0A        public void write(byte%5B%5D b) throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.write(b);%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A%0A        @Override%0A        public void write(byte%5B%5D b, int off, int len) throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.write(b, off, len);%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A%0A        @Override%0A        public void flush() throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.flush();%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A%0A        @Override%0A        public void close() throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.close();%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A
     

Revert "Make OutputCapturer thread safe"