/* * ==================================================================== * Copyright (c) 2004-2010 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.internal.wc.patch; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.tmatesoft.svn.core.SVNException; /** * Data type to manage parsing of patches. * * @version 1.3 * @author TMate Software Ltd. */ public class SVNPatch { public static final String MINUS = "--- "; public static final String PLUS = "+++ "; public static final String ATAT = "@@"; /** Path to the patch file. */ private File path; /** The patch file itself. */ private SVNPatchFileStream patchFile; /** * The old and new file names as retrieved from the patch file. These paths * are UTF-8 encoded and canonicalized, but otherwise left unchanged from * how they appeared in the patch file. */ private File oldFilename; private File newFilename; /** * An array containing an svn_hunk_t object for each hunk parsed from the * patch. */ private List hunks; public File getPath() { return path; } public SVNPatchFileStream getPatchFile() { return patchFile; } public File getOldFilename() { return oldFilename; } public File getNewFilename() { return newFilename; } public List getHunks() { return hunks; } public void close() throws IOException { if (hunks != null) { int hunksCount = hunks.size(); if (hunksCount > 0) { for (int i = 0; i < hunksCount; i++) { final SVNPatchHunk hunk = (SVNPatchHunk) hunks.get(i); hunk.close(); } } } } /** * Return the next PATCH in PATCH_FILE. * * If no patch can be found, set PATCH to NULL. * * @throws SVNException * @throws IOException */ public static SVNPatch parseNextPatch(SVNPatchFileStream patchFile) throws SVNException, IOException { if (patchFile.isEOF()) { /* No more patches here. */ return null; } /* Get the patch's filename. */ final File patchPath = patchFile.getPath(); /* Record what we already know about the patch. */ final SVNPatch patch = new SVNPatch(); patch.patchFile = patchFile; patch.path = patchPath; String indicator = MINUS; boolean eof = false, in_header = false; final StringBuffer lineBuf = new StringBuffer(); do { lineBuf.setLength(0); /* Read a line from the stream. */ eof = patchFile.readLine(lineBuf); final String line = lineBuf.toString(); /* See if we have a diff header. */ if (!eof && line.length() > indicator.length() && line.startsWith(indicator)) { /* * If we can find a tab, it separates the filename from the rest * of the line which we can discard. */ final int tab = line.indexOf('\t'); final File filePath = new File(line.substring(indicator.length(), tab > 0 ? tab : line.length())); if ((!in_header) && MINUS.equals(indicator)) { /* First line of header contains old filename. */ patch.oldFilename = filePath; indicator = PLUS; in_header = true; } else if (in_header && PLUS.equals(indicator)) { /* Second line of header contains new filename. */ patch.newFilename = filePath; in_header = false; break; /* All good! */ } else { in_header = false; } } } while (!eof); if (patch.oldFilename == null || patch.newFilename == null) { /* Something went wrong, just discard the result. */ return null; } /* Parse hunks. */ patch.hunks = new ArrayList(10); SVNPatchHunk hunk; do { hunk = SVNPatchHunk.parseNextHunk(patch); if (hunk != null) { patch.hunks.add(hunk); } } while (hunk != null); /* * Usually, hunks appear in the patch sorted by their original line * offset. But just in case they weren't parsed in this order for some * reason, we sort them so that our caller can assume that hunks are * sorted as if parsed from a usual patch. */ Collections.sort(patch.hunks, SVNPatchHunk.COMPARATOR); return patch; } }