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.Closeable;
023import java.io.File;
024import java.io.FileInputStream;
025import java.io.FileOutputStream;
026import java.io.IOException;
027import java.io.InputStream;
028import java.io.OutputStream;
029import java.nio.ByteBuffer;
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    private static void close( Closeable closeable )
044    {
045        if ( closeable != null )
046        {
047            try
048            {
049                closeable.close();
050            }
051            catch ( IOException e )
052            {
053                // too bad but who cares
054            }
055        }
056    }
057
058    /**
059     * Thread-safe variant of {@link File#mkdirs()}. Creates the directory named by the given abstract pathname,
060     * including any necessary but nonexistent parent directories. Note that if this operation fails it may have
061     * succeeded in creating some of the necessary parent directories.
062     * 
063     * @param directory The directory to create, may be {@code null}.
064     * @return {@code true} if and only if the directory was created, along with all necessary parent directories;
065     *         {@code false} otherwise
066     */
067    public boolean mkdirs( File directory )
068    {
069        if ( directory == null )
070        {
071            return false;
072        }
073
074        if ( directory.exists() )
075        {
076            return false;
077        }
078        if ( directory.mkdir() )
079        {
080            return true;
081        }
082
083        File canonDir;
084        try
085        {
086            canonDir = directory.getCanonicalFile();
087        }
088        catch ( IOException e )
089        {
090            return false;
091        }
092
093        File parentDir = canonDir.getParentFile();
094        return ( parentDir != null && ( mkdirs( parentDir ) || parentDir.exists() ) && canonDir.mkdir() );
095    }
096
097    public void write( File target, String data )
098        throws IOException
099    {
100        mkdirs( target.getAbsoluteFile().getParentFile() );
101
102        OutputStream fos = null;
103        try
104        {
105            fos = new FileOutputStream( target );
106
107            if ( data != null )
108            {
109                fos.write( data.getBytes( "UTF-8" ) );
110            }
111
112            // allow output to report any flush/close errors
113            fos.close();
114        }
115        finally
116        {
117            close( fos );
118        }
119    }
120
121    public void write( File target, InputStream source )
122        throws IOException
123    {
124        mkdirs( target.getAbsoluteFile().getParentFile() );
125
126        OutputStream fos = null;
127        try
128        {
129            fos = new FileOutputStream( target );
130
131            copy( fos, source, null );
132
133            // allow output to report any flush/close errors
134            fos.close();
135        }
136        finally
137        {
138            close( fos );
139        }
140    }
141
142    public void copy( File source, File target )
143        throws IOException
144    {
145        copy( source, target, null );
146    }
147
148    public long copy( File source, File target, ProgressListener listener )
149        throws IOException
150    {
151        long total = 0;
152
153        InputStream fis = null;
154        OutputStream fos = null;
155        try
156        {
157            fis = new FileInputStream( source );
158
159            mkdirs( target.getAbsoluteFile().getParentFile() );
160
161            fos = new FileOutputStream( target );
162
163            total = copy( fos, fis, listener );
164
165            // allow output to report any flush/close errors
166            fos.close();
167        }
168        finally
169        {
170            close( fis );
171            close( fos );
172        }
173
174        return total;
175    }
176
177    private long copy( OutputStream os, InputStream is, ProgressListener listener )
178        throws IOException
179    {
180        long total = 0;
181
182        ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
183        byte[] array = buffer.array();
184
185        while ( true )
186        {
187            int bytes = is.read( array );
188            if ( bytes < 0 )
189            {
190                break;
191            }
192
193            os.write( array, 0, bytes );
194
195            total += bytes;
196
197            if ( listener != null && bytes > 0 )
198            {
199                try
200                {
201                    buffer.rewind();
202                    buffer.limit( bytes );
203                    listener.progressed( buffer );
204                }
205                catch ( Exception e )
206                {
207                    // too bad
208                }
209            }
210        }
211
212        return total;
213    }
214
215    public void move( File source, File target )
216        throws IOException
217    {
218        if ( !source.renameTo( target ) )
219        {
220            copy( source, target );
221
222            target.setLastModified( source.lastModified() );
223
224            source.delete();
225        }
226    }
227
228}