001/*
002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Julien Carsique
016 *
017 */
018
019package org.nuxeo.connect.update.task.standalone.commands;
020
021import java.io.BufferedReader;
022import java.io.File;
023import java.io.FileReader;
024import java.io.IOException;
025import java.util.Map;
026
027import org.nuxeo.common.utils.FileUtils;
028import org.nuxeo.connect.update.PackageException;
029import org.nuxeo.connect.update.ValidationStatus;
030import org.nuxeo.connect.update.task.Command;
031import org.nuxeo.connect.update.task.Task;
032import org.nuxeo.connect.update.task.standalone.UninstallTask;
033import org.nuxeo.connect.update.util.IOUtils;
034import org.nuxeo.connect.update.xml.XmlWriter;
035import org.w3c.dom.Element;
036
037/**
038 * Rollback command for {@link Append} and {@link Copy} (with {@link Copy#append}=true) commands.
039 *
040 * @since 5.5
041 */
042public class UnAppend extends AbstractCommand {
043
044    public static final String ID = "unappend";
045
046    private static final String newLine = System.getProperty("line.separator");
047
048    private File contentToRemove;
049
050    private File fromFile;
051
052    public UnAppend() {
053        this(ID);
054    }
055
056    protected UnAppend(String id) {
057        super(id);
058    }
059
060    /**
061     * @param contentToRemove File which content must be removed.
062     * @param fromFile Destination file from which content is removed.
063     */
064    public UnAppend(File contentToRemove, File fromFile) {
065        this(ID);
066        this.contentToRemove = contentToRemove;
067        this.fromFile = fromFile;
068    }
069
070    @Override
071    public void writeTo(XmlWriter writer) {
072        writer.start(ID);
073        if (contentToRemove != null) {
074            writer.attr("contentToRemove", contentToRemove.getAbsolutePath());
075        }
076        if (fromFile != null) {
077            writer.attr("fromFile", fromFile.getAbsolutePath());
078        }
079        writer.end();
080    }
081
082    @Override
083    protected Command doRun(Task task, Map<String, String> prefs) throws PackageException {
084        BufferedReader brToRemove = null, brFromFile = null;
085        File bak;
086        StringBuilder linesToKeep = new StringBuilder();
087        StringBuilder linesToRemove = new StringBuilder();
088        try {
089            try {
090                brToRemove = new BufferedReader(new FileReader(contentToRemove));
091                String lineToRemove = brToRemove.readLine();
092                brFromFile = new BufferedReader(new FileReader(fromFile));
093                String lineToCheck;
094                boolean found = false;
095                while ((lineToCheck = brFromFile.readLine()) != null) {
096
097                    if (lineToCheck.equals(lineToRemove)) {
098                        // Maybe the line to remove, but let's check the next
099                        // lines
100                        found = true;
101                        linesToRemove.append(lineToCheck + newLine);
102                        lineToRemove = brToRemove.readLine();
103                    } else {
104                        if (lineToRemove != null && found) {
105                            // Previously found lines must finally be kept
106                            found = false;
107                            linesToKeep.append(linesToRemove.toString());
108                            linesToRemove = new StringBuilder();
109                            org.apache.commons.io.IOUtils.closeQuietly(brToRemove);
110                            brToRemove = new BufferedReader(new FileReader(contentToRemove));
111                        }
112                        linesToKeep.append(lineToCheck + newLine);
113                    }
114                }
115                if (lineToRemove != null) {
116                    throw new PackageException("All lines to remove were not found.");
117                }
118            } finally {
119                org.apache.commons.io.IOUtils.closeQuietly(brToRemove);
120                org.apache.commons.io.IOUtils.closeQuietly(brFromFile);
121            }
122            if (task instanceof UninstallTask) {
123                bak = null;
124            } else {
125                bak = IOUtils.backup(task.getPackage(), contentToRemove);
126            }
127            FileUtils.writeFile(fromFile, linesToKeep.toString());
128            return new Append(bak, fromFile);
129        } catch (PackageException e) {
130            throw e;
131        } catch (IOException e) {
132            throw new PackageException(e);
133        }
134    }
135
136    @Override
137    protected void doValidate(Task task, ValidationStatus status) throws PackageException {
138        if (contentToRemove == null || fromFile == null) {
139            status.addError("Cannot execute command in installer."
140                    + " Invalid unappend syntax: contentToRemove or fromFile was not specified.");
141        }
142    }
143
144    @Override
145    public void readFrom(Element element) throws PackageException {
146        String v = element.getAttribute("contentToRemove");
147        if (v.length() > 0) {
148            contentToRemove = new File(v);
149        }
150        v = element.getAttribute("fromFile");
151        if (v.length() > 0) {
152            fromFile = new File(v);
153        }
154    }
155
156}