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 */
035public class HgDiffConsumer extends HgConsumer {
036
037    // private static final String MODIFIED_FILE_TOKEN = "=== modified file ";
038
039    private static final String INDEX_TOKEN = "diff -r ";
040
041    private static final String FILE_SEPARATOR_TOKEN = "===";
042
043    private static final String START_REVISION_TOKEN = "---";
044
045    private static final String END_REVISION_TOKEN = "+++";
046
047    private static final String ADDED_LINE_TOKEN = "+";
048
049    private static final String REMOVED_LINE_TOKEN = "-";
050
051    private static final String UNCHANGED_LINE_TOKEN = " ";
052
053    private static final String CHANGE_SEPARATOR_TOKEN = "@@";
054
055    private static final String NO_NEWLINE_TOKEN = "\\ No newline at end of file";
056
057    private static final int HASH_ID_LEN = 12;
058
059    private String currentFile;
060
061    private StringBuilder currentDifference;
062
063    private final List<ScmFile> changedFiles = new ArrayList<>();
064
065    private final Map<String, CharSequence> differences = new HashMap<>();
066
067    private final StringBuilder patch = new StringBuilder();
068
069    @SuppressWarnings("unused")
070    private final File workingDirectory;
071
072    public HgDiffConsumer(File workingDirectory) {
073        this.workingDirectory = workingDirectory;
074    }
075
076    // ----------------------------------------------------------------------
077    // StreamConsumer Implementation
078    // ----------------------------------------------------------------------
079
080    /**
081     * {@inheritDoc}
082     */
083    public void consumeLine(String line) {
084        if (line.startsWith(INDEX_TOKEN)) {
085            // start a new file
086            currentFile = line.substring(INDEX_TOKEN.length() + HASH_ID_LEN + 1);
087
088            changedFiles.add(new ScmFile(currentFile, ScmFileStatus.MODIFIED));
089
090            currentDifference = new StringBuilder();
091
092            differences.put(currentFile, currentDifference);
093
094            patch.append(line).append("\n");
095
096            return;
097        }
098
099        if (currentFile == null) {
100            if (logger.isWarnEnabled()) {
101                logger.warn("Unparseable line: '" + line + "'");
102            }
103            patch.append(line).append("\n");
104            return;
105        }
106
107        if (line.startsWith(FILE_SEPARATOR_TOKEN)) {
108            // skip
109            patch.append(line).append("\n");
110        } else if (line.startsWith(START_REVISION_TOKEN)) {
111            // skip, though could parse to verify filename, start revision
112            patch.append(line).append("\n");
113        } else if (line.startsWith(END_REVISION_TOKEN)) {
114            // skip, though could parse to verify filename, end revision
115            patch.append(line).append("\n");
116        } else if (line.startsWith(ADDED_LINE_TOKEN)
117                || line.startsWith(REMOVED_LINE_TOKEN)
118                || line.startsWith(UNCHANGED_LINE_TOKEN)
119                || line.startsWith(CHANGE_SEPARATOR_TOKEN)
120                || line.equals(NO_NEWLINE_TOKEN)) {
121            // add to buffer
122            currentDifference.append(line).append("\n");
123            patch.append(line).append("\n");
124        } else {
125            // TODO: handle property differences
126
127            if (logger.isWarnEnabled()) {
128                logger.warn("Unparseable line: '" + line + "'");
129            }
130            patch.append(line).append("\n");
131            // skip to next file
132            currentFile = null;
133            currentDifference = null;
134        }
135    }
136
137    public List<ScmFile> getChangedFiles() {
138        return changedFiles;
139    }
140
141    public Map<String, CharSequence> getDifferences() {
142        return differences;
143    }
144
145    public String getPatch() {
146        return patch.toString();
147    }
148}