001package org.eclipse.aether.internal.impl;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.FileOutputStream;
025import java.io.IOException;
026import java.io.InputStream;
027import java.io.OutputStream;
028import java.nio.Buffer;
029import java.nio.ByteBuffer;
030import java.nio.charset.StandardCharsets;
031
032import javax.inject.Named;
033import javax.inject.Singleton;
034
035import org.eclipse.aether.spi.io.FileProcessor;
036
037/**
038 * A utility class helping with file-based operations.
039 */
040@Singleton
041@Named
042public class DefaultFileProcessor
043    implements FileProcessor
044{
045
046    /**
047     * Thread-safe variant of {@link File#mkdirs()}. Creates the directory named by the given abstract pathname,
048     * including any necessary but nonexistent parent directories. Note that if this operation fails it may have
049     * succeeded in creating some of the necessary parent directories.
050     *
051     * @param directory The directory to create, may be {@code null}.
052     * @return {@code true} if and only if the directory was created, along with all necessary parent directories;
053     *         {@code false} otherwise
054     */
055    public boolean mkdirs( File directory )
056    {
057        if ( directory == null )
058        {
059            return false;
060        }
061
062        if ( directory.exists() )
063        {
064            return false;
065        }
066        if ( directory.mkdir() )
067        {
068            return true;
069        }
070
071        File canonDir;
072        try
073        {
074            canonDir = directory.getCanonicalFile();
075        }
076        catch ( IOException e )
077        {
078            return false;
079        }
080
081        File parentDir = canonDir.getParentFile();
082        return ( parentDir != null && ( mkdirs( parentDir ) || parentDir.exists() ) && canonDir.mkdir() );
083    }
084
085    public void write( File target, String data )
086        throws IOException
087    {
088        mkdirs( target.getAbsoluteFile().getParentFile() );
089
090        OutputStream out = null;
091        try
092        {
093            out = new FileOutputStream( target );
094
095            if ( data != null )
096            {
097                out.write( data.getBytes( StandardCharsets.UTF_8 ) );
098            }
099
100            out.close();
101            out = null;
102        }
103        finally
104        {
105            try
106            {
107                if ( out != null )
108                {
109                    out.close();
110                }
111            }
112            catch ( final IOException e )
113            {
114                // Suppressed due to an exception already thrown in the try block.
115            }
116        }
117    }
118
119    public void write( File target, InputStream source )
120        throws IOException
121    {
122        mkdirs( target.getAbsoluteFile().getParentFile() );
123
124        OutputStream out = null;
125        try
126        {
127            out = new FileOutputStream( target );
128
129            copy( out, source, null );
130
131            out.close();
132            out = null;
133        }
134        finally
135        {
136            try
137            {
138                if ( out != null )
139                {
140                    out.close();
141                }
142            }
143            catch ( final IOException e )
144            {
145                // Suppressed due to an exception already thrown in the try block.
146            }
147        }
148    }
149
150    public void copy( File source, File target )
151        throws IOException
152    {
153        copy( source, target, null );
154    }
155
156    public long copy( File source, File target, ProgressListener listener )
157        throws IOException
158    {
159        long total = 0L;
160
161        InputStream in = null;
162        OutputStream out = null;
163        try
164        {
165            in = new FileInputStream( source );
166
167            mkdirs( target.getAbsoluteFile().getParentFile() );
168
169            out = new FileOutputStream( target );
170
171            total = copy( out, in, listener );
172
173            out.close();
174            out = null;
175
176            in.close();
177            in = null;
178        }
179        finally
180        {
181            try
182            {
183                if ( out != null )
184                {
185                    out.close();
186                }
187            }
188            catch ( final IOException e )
189            {
190                // Suppressed due to an exception already thrown in the try block.
191            }
192            finally
193            {
194                try
195                {
196                    if ( in != null )
197                    {
198                        in.close();
199                    }
200                }
201                catch ( final IOException e )
202                {
203                    // Suppressed due to an exception already thrown in the try block.
204                }
205            }
206        }
207
208        return total;
209    }
210
211    private long copy( OutputStream os, InputStream is, ProgressListener listener )
212        throws IOException
213    {
214        long total = 0L;
215
216        ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
217        byte[] array = buffer.array();
218
219        while ( true )
220        {
221            int bytes = is.read( array );
222            if ( bytes < 0 )
223            {
224                break;
225            }
226
227            os.write( array, 0, bytes );
228
229            total += bytes;
230
231            if ( listener != null && bytes > 0 )
232            {
233                try
234                {
235                    ( (Buffer) buffer ).rewind();
236                    ( (Buffer) buffer ).limit( bytes );
237                    listener.progressed( buffer );
238                }
239                catch ( Exception e )
240                {
241                    // too bad
242                }
243            }
244        }
245
246        return total;
247    }
248
249    public void move( File source, File target )
250        throws IOException
251    {
252        if ( !source.renameTo( target ) )
253        {
254            copy( source, target );
255
256            target.setLastModified( source.lastModified() );
257
258            source.delete();
259        }
260    }
261
262}