package com.perforce.cvs.asset; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.perforce.common.Stats; import com.perforce.common.StatsType; import com.perforce.common.asset.AssetWriter; import com.perforce.config.CFG; import com.perforce.config.Config; import com.perforce.cvs.parser.RcsDeltaAction; import com.perforce.cvs.parser.RcsReader; import com.perforce.cvs.parser.rcstypes.RcsObjectBlock; import com.perforce.cvs.parser.rcstypes.RcsObjectDelta; import com.perforce.cvs.parser.rcstypes.RcsObjectNum; import com.perforce.cvs.parser.rcstypes.RcsObjectNumList; import com.perforce.svn.parser.Content; public class CvsContentReader { private static Logger logger = LoggerFactory .getLogger(CvsContentReader.class); private RcsReader rcsDelta; private RcsObjectNum rcsHEAD; private RcsObjectBlock rcsBlock; public CvsContentReader(RcsReader rcs) { this.rcsDelta = rcs; this.rcsHEAD = rcs.getAdmin().getID(); RcsObjectDelta deltaHEAD = rcs.getDelta(rcsHEAD); this.rcsBlock = deltaHEAD.getBlock(); } /** * Writes out all RCS text revisions in full. Stores the text content in a * temporary directory, using the RCS id as a file name. * * @param id * @throws Exception */ public void cacheContent() throws Exception { cacheContent(rcsHEAD, rcsHEAD, this.rcsBlock); } /** * [RECURSIVE] * * @param id * @throws Exception */ private void cacheContent(RcsObjectNum id, RcsObjectNum last, RcsObjectBlock fullBlock) throws Exception { boolean parseError = false; RcsObjectBlock lastBlock = new RcsObjectBlock(fullBlock); do { if (logger.isDebugEnabled()) { logger.debug("processing: " + last + " > " + id); } RcsObjectDelta delta = rcsDelta.getDelta(id); // build tmp path from rev id and base path String tmp = (String) Config.get(CFG.CVS_TMPDIR); String base = rcsDelta.getPath(); String path = tmp + "/" + base + "/" + id; // undelta text; skip HEAD as it is already in full text try { if (parseError) { throw new Exception("Exception due to previous RCS errors."); } if (!rcsHEAD.equals(id)) { RcsObjectBlock blockDelta = delta.getBlock(); if (blockDelta != null) { lastBlock = undelta(lastBlock, blockDelta); } else { logger.warn("No data block: " + base + " " + delta.getID()); Stats.inc(StatsType.warningCount); } } // write blockFull to tmp file AssetWriter asset = new AssetWriter(path); Content content = new Content(lastBlock); asset.write(content); } catch (Exception e) { logger.warn("RCS parse error on: " + base + " " + delta.getID()); Stats.inc(StatsType.warningCount); // write blockFull to tmp file File dummy = new File(path); new FileOutputStream(dummy).close(); parseError = true; } // recurse on branches RcsObjectNumList tags = delta.getBranches(); if (!tags.isEmpty()) { for (RcsObjectNum tag : tags.getList()) { cacheContent(tag, id, lastBlock); } } last = id; id = delta.getNext(); } while (id != null); } private List<RcsDeltaAction> parse(RcsObjectBlock delta) throws Exception { Iterator<ByteArrayOutputStream> lines = delta.iterator(); // exit early if nothing to process if (!lines.hasNext()) { return new ArrayList<RcsDeltaAction>(); } ByteArrayOutputStream line = lines.next(); RcsDeltaAction action = new RcsDeltaAction(line); switch (action.getAction()) { case ADD: case DELETE: return parseDeltas(delta); case TEXT: return new ArrayList<RcsDeltaAction>(); default: StringBuffer sb = new StringBuffer(); sb.append("unknown type: " + action.getAction()); logger.error(sb.toString()); throw new Exception(sb.toString()); } } /** * Reads only the delta commands and skips over the correct number of text * lines. * * @param lines * @return * @throws Exception */ private List<RcsDeltaAction> parseDeltas(RcsObjectBlock delta) throws Exception { Iterator<ByteArrayOutputStream> lines = delta.iterator(); List<RcsDeltaAction> list = new ArrayList<RcsDeltaAction>(); while (lines.hasNext()) { ByteArrayOutputStream line = lines.next(); RcsDeltaAction action = new RcsDeltaAction(line); switch (action.getAction()) { case ADD: list.add(action); for (int i = 0; i < action.getLength(); i++) { // read lines and add to action if (lines.hasNext()) { line = lines.next(); action.addLine(line); } } break; case DELETE: list.add(action); break; default: StringBuffer sb = new StringBuffer(); sb.append("unmatched line: " + line); logger.error(sb.toString()); throw new Exception(sb.toString()); } } return list; } /** * Rebuilds the delta given the full file * * @param full * @param delta * @return * @throws Exception */ private RcsObjectBlock undelta(RcsObjectBlock full, RcsObjectBlock delta) throws Exception { List<RcsDeltaAction> list = parse(delta); // If full is empty and no parsed deltas, then return the delta if (full.isEmpty() && list.isEmpty()) { return delta; } // Apply deltas in reverse order to preserve index references Collections.reverse(list); for (RcsDeltaAction d : list) { if (logger.isTraceEnabled()) { logger.trace("... " + d); } switch (d.getAction()) { case ADD: full.insert(d.getLine(), d.getBlock()); break; case DELETE: full.remove(d.getLine(), d.getLength()); break; default: StringBuffer sb = new StringBuffer(); sb.append("unknown type: " + d.getAction()); logger.error(sb.toString()); throw new Exception(sb.toString()); } } if (logger.isTraceEnabled()) { logger.trace("result: " + full); } return full; } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 13876 | Paul Allen | Rename/move file(s) | ||
//guest/paul_allen/p4convert-maven/src/com/perforce/cvs/asset/CvsContentReader.java | |||||
#1 | 13873 | Paul Allen | Branching using p4convert-maven | ||
//guest/perforce_software/p4convert/src/com/perforce/cvs/asset/CvsContentReader.java | |||||
#7 | 11745 | Paul Allen |
CVS: Parse non-standard RCS file, where 'log' and 'text' are on the same line. Support alternative delete method - added test case 054 to support parse changes - added test case 055 to support alternatice delete method |
||
#6 | 11453 | Paul Allen |
CVS: Write empty files for corrupt RCS revisions. Fix for CVS crash on corrupt RCS data. Unable to process file: /Users/Workspaces/cvsroot/xxx,v java.lang.IndexOutOfBoundsException: Index: 24405, Size: 11725 at java.util.ArrayList.rangeCheckForAdd(ArrayList.java:612) at java.util.ArrayList.addAll(ArrayList.java:554) at com.perforce.cvs.parser.rcstypes.RcsObjectBlock.insert(RcsObjectBlock.java:31) at com.perforce.cvs.asset.CvsContentReader.undelta(CvsContentReader.java:214) at com.perforce.cvs.asset.CvsContentReader.cacheContent(CvsContentReader.java:98) at com.perforce.cvs.asset.CvsContentReader.cacheContent(CvsContentReader.java:71) at com.perforce.cvs.process.CvsProcessChange.buildRevisionList(CvsProcessChange.java:247) at com.perforce.cvs.process.CvsProcessChange.processChange(CvsProcessChange.java:74) at com.perforce.common.process.ProcessChange.runSingle(ProcessChange.java:90) at com.perforce.common.process.ProcessChange.call(ProcessChange.java:53) at com.perforce.common.process.ProcessChange.call(ProcessChange.java:20) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) at java.util.concurrent.FutureTask.run(FutureTask.java:166) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) at java.lang.Thread.run(Thread.java:722) |
||
#5 | 10987 | Paul Allen |
CVS new RCS undelta parser. Searches for the exact number of added lines, before looking for the next delta action. Should read RCS data with versioned RCS data inside it. - Updated logging. |
||
#4 | 10957 | Paul Allen |
CVS parse RCS delta fix to regex. - added more debugging |
||
#3 | 10877 | Paul Allen |
Fix to add a warning if data block is missing. Unbranch operation fixes to follow (when unrelated files are added to branches off main) |
||
#2 | 10497 | Paul Allen |
New low-level RCS reader using a byte[] to manage CVS lines. Designed to help with the processing of BINARY data in RCS files. The line reading code still looks for a unix style '\n', but has a MAX LINE (hard coded to 10K). The RcsObjectBlock uses a ByteArrayOutputStream to store lines and parsers uses byte logic. (passes basic cvs/svn unit tests) |
||
#1 | 9807 | Paul Allen | Initial import of p4-convert (from change 894340) |