001/*
002 * Copyright (c) 2002-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.geon;
031
032import java.io.File;
033import java.io.FileInputStream;
034import java.io.FileOutputStream;
035import java.io.IOException;
036import java.io.InputStream;
037import java.io.OutputStream;
038
039import ptolemy.actor.TypedAtomicActor;
040import ptolemy.actor.TypedIOPort;
041import ptolemy.data.BooleanToken;
042import ptolemy.data.StringToken;
043import ptolemy.data.expr.FileParameter;
044import ptolemy.data.expr.Parameter;
045import ptolemy.data.type.BaseType;
046import ptolemy.gui.GraphicalMessageHandler;
047import ptolemy.kernel.CompositeEntity;
048import ptolemy.kernel.util.IllegalActionException;
049import ptolemy.kernel.util.NameDuplicationException;
050
051//////////////////////////////////////////////////////////////////////////
052//// FileCopy
053/**
054 * This actor copies a source file to a destination file and outputs the
055 * destination file URL.
056 * 
057 * The source and destination file paths can be accepted either through a port
058 * or a parameter.
059 * 
060 * If the append attribute has value true, then the file will be appended to. If
061 * it has value false, then if the file exists, the user will be queried for
062 * permission to overwrite, and if granted, the file will be overwritten.
063 * 
064 * If the confirmOverwrite parameter has value false, then this actor will
065 * overwrite the specified file if it exists without asking. If true (the
066 * default), then if the file exists, then this actor will ask for confirmation
067 * before overwriting.
068 * 
069 * @see FileParameter
070 * @author Efrat Jaeger
071 * @version $Id: FileCopy.java 24234 2010-05-06 05:21:26Z welker $
072 * @since Ptolemy II 4.0.1
073 */
074
075public class FileCopy extends TypedAtomicActor {
076
077        /**
078         * Construct an actor with the given container and name.
079         * 
080         * @param container
081         *            The container.
082         * @param name
083         *            The name of this actor.
084         * @exception IllegalActionException
085         *                If the actor cannot be contained by the proposed
086         *                container.
087         * @exception NameDuplicationException
088         *                If the container already has an actor with this name.
089         */
090        public FileCopy(CompositeEntity container, String name)
091                        throws IllegalActionException, NameDuplicationException {
092                super(container, name);
093
094                sourceFile = new TypedIOPort(this, "sourceFile", true, false);
095                sourceFile.setTypeEquals(BaseType.STRING);
096
097                destinationFile = new TypedIOPort(this, "destinationFile", true, false);
098                destinationFile.setTypeEquals(BaseType.STRING);
099
100                outputFile = new TypedIOPort(this, "outputFile", false, true);
101                outputFile.setTypeEquals(BaseType.STRING);
102
103                sourceFileParam = new FileParameter(this, "sourceFileParam");
104                sourceFileParam.setDisplayName("source File");
105
106                destFileParam = new FileParameter(this, "destFileParam");
107                destFileParam.setDisplayName("destination File");
108
109                new Parameter(sourceFileParam, "allowDirectories", BooleanToken.TRUE);
110                new Parameter(destFileParam, "allowDirectories", BooleanToken.TRUE);
111
112                append = new Parameter(this, "append");
113                append.setTypeEquals(BaseType.BOOLEAN);
114                append.setToken(BooleanToken.FALSE);
115
116                confirmOverwrite = new Parameter(this, "confirmOverwrite");
117                confirmOverwrite.setTypeEquals(BaseType.BOOLEAN);
118                confirmOverwrite.setToken(BooleanToken.TRUE);
119                
120                recursive = new Parameter(this, "recursive");
121                recursive.setTypeEquals(BaseType.BOOLEAN);
122                recursive.setToken(BooleanToken.TRUE);
123
124                _attachText("_iconDescription", "<svg>\n"
125                                + "<rect x=\"-25\" y=\"-20\" " + "width=\"50\" height=\"40\" "
126                                + "style=\"fill:white\"/>\n"
127                                + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10"
128                                + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n"
129                                + "</svg>\n");
130        }
131
132        // /////////////////////////////////////////////////////////////////
133        // // ports and parameters ////
134
135        /**
136         * Source file.
137         */
138        public TypedIOPort sourceFile;
139
140        /**
141         * Destination file.
142         */
143        public TypedIOPort destinationFile;
144
145        /**
146         * Output destination file URL.
147         */
148        public TypedIOPort outputFile;
149
150        /**
151         * Source file name or URL. This is a string with any form accepted by
152         * FileParameter.
153         * 
154         * @see FileParameter
155         */
156        public FileParameter sourceFileParam;
157
158        /**
159         * Destination file name or URL. This is a string with any form accepted by
160         * FileParameter.
161         * 
162         * @see FileParameter
163         */
164        public FileParameter destFileParam;
165
166        /**
167         * If true, then append to the specified file. If false (the default), then
168         * overwrite any preexisting file after asking the user for permission.
169         */
170        public Parameter append;
171
172        /**
173         * If false, then overwrite the specified file if it exists without asking.
174         * If true (the default), then if the file exists, ask for confirmation
175         * before overwriting.
176         */
177        public Parameter confirmOverwrite;
178        
179        /**
180         * If false, then only copy the files, not the sub-directories, in the source location. 
181         */
182        public Parameter recursive;
183
184        /**
185         * Copy the source file to the destination file. Broadcast the destination
186         * file path.
187         * 
188         * @exception IllegalActionException
189         *                If there's no director.
190         */
191        public void fire() throws IllegalActionException {
192                File _sourceFile = null, _destFile = null;
193                String fileName = "";
194
195                // get source file.
196                if (sourceFile.getWidth() > 0) {
197                        fileName = ((StringToken) sourceFile.get(0)).stringValue();
198                        int lineEndInd = fileName.indexOf("\n");
199                        if (lineEndInd != -1) { // The string contains a CR.
200                                fileName = fileName.substring(0, lineEndInd);
201                        }
202                        sourceFileParam.setExpression(fileName);
203                }
204                _sourceFile = sourceFileParam.asFile();
205
206                if (!_sourceFile.exists()) {
207                        throw new IllegalActionException(this, "file " + fileName
208                                        + " doesn't exist.");
209                }
210
211                // get dest file.
212                fileName = "";
213                if (destinationFile.getWidth() > 0) {
214                        fileName = ((StringToken) destinationFile.get(0)).stringValue();
215                        int lineEndInd = fileName.indexOf("\n");
216                        if (lineEndInd != -1) { // The string contains a CR.
217                                fileName = fileName.substring(0, lineEndInd);
218                        }
219                        destFileParam.setExpression(fileName);
220                }
221                _destFile = destFileParam.asFile();
222
223                boolean appendValue = ((BooleanToken) append.getToken()).booleanValue();
224                boolean confirmOverwriteValue = ((BooleanToken) confirmOverwrite
225                                .getToken()).booleanValue();
226                boolean recursiveValue = ((BooleanToken) recursive
227                                .getToken()).booleanValue();
228                // Don't ask for confirmation in append mode, since there
229                // will be no loss of data.
230                if (_destFile.exists() && !appendValue && confirmOverwriteValue) {
231                        // Query for overwrite.
232                        // FIXME: This should be called in the event thread!
233                        // There is a chance of deadlock since it is not.
234                        if (!GraphicalMessageHandler.yesNoQuestion("OK to overwrite "
235                                        + _destFile + "?")) {
236                                throw new IllegalActionException(this,
237                                                "Please select another file name.");
238                        }
239                }
240
241                try {
242                        copyFiles(_sourceFile,_destFile, recursiveValue);
243
244                } catch (Exception ex) {
245                        throw new IllegalActionException(this, ex, "Error copying "
246                                        + _sourceFile.getPath() + " to " + _destFile.getPath()
247                                        + ".");
248                }
249                outputFile.broadcast(new StringToken(_destFile.getAbsolutePath()));
250        }
251        
252    void copyFiles(File sourceLocation , File targetLocation, boolean recursive)
253    throws IOException {
254        
255        if (sourceLocation.isDirectory()) {
256            if (!targetLocation.exists()) {
257                targetLocation.mkdir();
258            }
259            
260            String[] children = sourceLocation.list();
261            for (int i=0; i<children.length; i++) {
262                File sourceChildFile = new File(sourceLocation, children[i]);
263                if (recursive)
264                copyFiles(sourceChildFile,
265                        new File(targetLocation, children[i]), true);
266                else
267                {
268                        if (sourceChildFile.isFile())
269                        copyFiles(sourceChildFile,
270                            new File(targetLocation, children[i]), false);
271                }
272            }
273        } else {
274            
275            InputStream in = new FileInputStream(sourceLocation);
276            OutputStream out = new FileOutputStream(targetLocation);
277            
278            // Copy the bits from instream to outstream
279            byte[] buf = new byte[1024];
280            int len;
281            while ((len = in.read(buf)) > 0) {
282                out.write(buf, 0, len);
283            }
284            in.close();
285            out.close();
286        }
287    }
288    
289    void copyFile(File sourceLocation , File targetLocation)
290    throws IOException {
291        InputStream in = new FileInputStream(sourceLocation);
292        OutputStream out = new FileOutputStream(targetLocation);
293        
294        // Copy the bits from instream to outstream
295        byte[] buf = new byte[1024];
296        int len;
297        while ((len = in.read(buf)) > 0) {
298            out.write(buf, 0, len);
299        }
300        in.close();
301        out.close();
302    }
303
304}