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