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.ByteBuffer;
029import java.nio.charset.StandardCharsets;
030
031import javax.inject.Named;
032
033import org.eclipse.aether.spi.io.FileProcessor;
034
035/**
036 * A utility class helping with file-based operations.
037 */
038@Named
039public class DefaultFileProcessor
040    implements FileProcessor
041{
042
043    /**
044     * Thread-safe variant of {@link File#mkdirs()}. Creates the directory named by the given abstract pathname,
045     * including any necessary but nonexistent parent directories. Note that if this operation fails it may have
046     * succeeded in creating some of the necessary parent directories.
047     * 
048     * @param directory The directory to create, may be {@code null}.
049     * @return {@code true} if and only if the directory was created, along with all necessary parent directories;
050     *         {@code false} otherwise
051     */
052    public boolean mkdirs( File directory )
053    {
054        if ( directory == null )
055        {
056            return false;
057        }
058
059        if ( directory.exists() )
060        {
061            return false;
062        }
063        if ( directory.mkdir() )
064        {
065            return true;
066        }
067
068        File canonDir;
069        try
070        {
071            canonDir = directory.getCanonicalFile();
072        }
073        catch ( IOException e )
074        {
075            return false;
076        }
077
078        File parentDir = canonDir.getParentFile();
079        return ( parentDir != null && ( mkdirs( parentDir ) || parentDir.exists() ) && canonDir.mkdir() );
080    }
081
082    public void write( File target, String data )
083        throws IOException
084    {
085        mkdirs( target.getAbsoluteFile().getParentFile() );
086
087        OutputStream out = null;
088        try
089        {
090            out = new FileOutputStream( target );
091
092            if ( data != null )
093            {
094                out.write( data.getBytes( StandardCharsets.UTF_8 ) );
095            }
096
097            out.close();
098            out = null;
099        }
100        finally
101        {
102            try
103            {
104                if ( out != null )
105                {
106                    out.close();
107                }
108            }
109            catch ( final IOException e )
110            {
111                // Suppressed due to an exception already thrown in the try block.
112            }
113        }
114    }
115
116    public void write( File target, InputStream source )
117        throws IOException
118    {
119        mkdirs( target.getAbsoluteFile().getParentFile() );
120
121        OutputStream out = null;
122        try
123        {
124            out = new FileOutputStream( target );
125
126            copy( out, source, null );
127
128            out.close();
129            out = null;
130        }
131        finally
132        {
133            try
134            {
135                if ( out != null )
136                {
137                    out.close();
138                }
139            }
140            catch ( final IOException e )
141            {
142                // Suppressed due to an exception already thrown in the try block.
143            }
144        }
145    }
146
147    public void copy( File source, File target )
148        throws IOException
149    {
150        copy( source, target, null );
151    }
152
153    public long copy( File source, File target, ProgressListener listener )
154        throws IOException
155    {
156        long total = 0L;
157
158        InputStream in = null;
159        OutputStream out = null;
160        try
161        {
162            in = new FileInputStream( source );
163
164            mkdirs( target.getAbsoluteFile().getParentFile() );
165
166            out = new FileOutputStream( target );
167
168            total = copy( out, in, listener );
169
170            out.close();
171            out = null;
172
173            in.close();
174            in = null;
175        }
176        finally
177        {
178            try
179            {
180                if ( out != null )
181                {
182                    out.close();
183                }
184            }
185            catch ( final IOException e )
186            {
187                // Suppressed due to an exception already thrown in the try block.
188            }
189            finally
190            {
191                try
192                {
193                    if ( in != null )
194                    {
195                        in.close();
196                    }
197                }
198                catch ( final IOException e )
199                {
200                    // Suppressed due to an exception already thrown in the try block.
201                }
202            }
203        }
204
205        return total;
206    }
207
208    private long copy( OutputStream os, InputStream is, ProgressListener listener )
209        throws IOException
210    {
211        long total = 0L;
212
213        ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
214        byte[] array = buffer.array();
215
216        while ( true )
217        {
218            int bytes = is.read( array );
219            if ( bytes < 0 )
220            {
221                break;
222            }
223
224            os.write( array, 0, bytes );
225
226            total += bytes;
227
228            if ( listener != null && bytes > 0 )
229            {
230                try
231                {
232                    buffer.rewind();
233                    buffer.limit( bytes );
234                    listener.progressed( buffer );
235                }
236                catch ( Exception e )
237                {
238                    // too bad
239                }
240            }
241        }
242
243        return total;
244    }
245
246    public void move( File source, File target )
247        throws IOException
248    {
249        if ( !source.renameTo( target ) )
250        {
251            copy( source, target );
252
253            target.setLastModified( source.lastModified() );
254
255            source.delete();
256        }
257    }
258
259}