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