001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.scm.provider.hg.command.diff;
020
021import java.io.File;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import org.apache.maven.scm.ScmFile;
028import org.apache.maven.scm.ScmFileStatus;
029import org.apache.maven.scm.provider.hg.command.HgConsumer;
030
031/**
032 * @author <a href="mailto:thurner.rupert@ymono.net">thurner rupert</a>
033 * @author Olivier Lamy
034 *
035 */
036public class HgDiffConsumer extends HgConsumer {
037
038    // private static final String MODIFIED_FILE_TOKEN = "=== modified file ";
039
040    private static final String INDEX_TOKEN = "diff -r ";
041
042    private static final String FILE_SEPARATOR_TOKEN = "===";
043
044    private static final String START_REVISION_TOKEN = "---";
045
046    private static final String END_REVISION_TOKEN = "+++";
047
048    private static final String ADDED_LINE_TOKEN = "+";
049
050    private static final String REMOVED_LINE_TOKEN = "-";
051
052    private static final String UNCHANGED_LINE_TOKEN = " ";
053
054    private static final String CHANGE_SEPARATOR_TOKEN = "@@";
055
056    private static final String NO_NEWLINE_TOKEN = "\\ No newline at end of file";
057
058    private static final int HASH_ID_LEN = 12;
059
060    private String currentFile;
061
062    private StringBuilder currentDifference;
063
064    private final List<ScmFile> changedFiles = new ArrayList<>();
065
066    private final Map<String, CharSequence> differences = new HashMap<>();
067
068    private final StringBuilder patch = new StringBuilder();
069
070    @SuppressWarnings("unused")
071    private final File workingDirectory;
072
073    public HgDiffConsumer(File workingDirectory) {
074        this.workingDirectory = workingDirectory;
075    }
076
077    // ----------------------------------------------------------------------
078    // StreamConsumer Implementation
079    // ----------------------------------------------------------------------
080
081    /** {@inheritDoc} */
082    public void consumeLine(String line) {
083        if (line.startsWith(INDEX_TOKEN)) {
084            // start a new file
085            currentFile = line.substring(INDEX_TOKEN.length() + HASH_ID_LEN + 1);
086
087            changedFiles.add(new ScmFile(currentFile, ScmFileStatus.MODIFIED));
088
089            currentDifference = new StringBuilder();
090
091            differences.put(currentFile, currentDifference);
092
093            patch.append(line).append("\n");
094
095            return;
096        }
097
098        if (currentFile == null) {
099            if (logger.isWarnEnabled()) {
100                logger.warn("Unparseable line: '" + line + "'");
101            }
102            patch.append(line).append("\n");
103            return;
104        }
105
106        if (line.startsWith(FILE_SEPARATOR_TOKEN)) {
107            // skip
108            patch.append(line).append("\n");
109        } else if (line.startsWith(START_REVISION_TOKEN)) {
110            // skip, though could parse to verify filename, start revision
111            patch.append(line).append("\n");
112        } else if (line.startsWith(END_REVISION_TOKEN)) {
113            // skip, though could parse to verify filename, end revision
114            patch.append(line).append("\n");
115        } else if (line.startsWith(ADDED_LINE_TOKEN)
116                || line.startsWith(REMOVED_LINE_TOKEN)
117                || line.startsWith(UNCHANGED_LINE_TOKEN)
118                || line.startsWith(CHANGE_SEPARATOR_TOKEN)
119                || line.equals(NO_NEWLINE_TOKEN)) {
120            // add to buffer
121            currentDifference.append(line).append("\n");
122            patch.append(line).append("\n");
123        } else {
124            // TODO: handle property differences
125
126            if (logger.isWarnEnabled()) {
127                logger.warn("Unparseable line: '" + line + "'");
128            }
129            patch.append(line).append("\n");
130            // skip to next file
131            currentFile = null;
132            currentDifference = null;
133        }
134    }
135
136    public List<ScmFile> getChangedFiles() {
137        return changedFiles;
138    }
139
140    public Map<String, CharSequence> getDifferences() {
141        return differences;
142    }
143
144    public String getPatch() {
145        return patch.toString();
146    }
147}