Package org.tmatesoft.hg.internal

Source Code of org.tmatesoft.hg.internal.ChangesetParser$CsetFactory

/*
* Copyright (c) 2010-2013 TMate Software Ltd
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* For information on how to redistribute this software under
* the terms of a license other than GNU General Public License
* contact TMate Software at support@hg4j.com
*/
package org.tmatesoft.hg.internal;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.tmatesoft.hg.core.Nodeid;
import org.tmatesoft.hg.core.SessionContext;
import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
import org.tmatesoft.hg.repo.HgInvalidDataFormatException;
import org.tmatesoft.hg.repo.HgRepository;

/**
* @see mercurial/changelog.py:read()
*
*      <pre>
*         format used:
*         nodeid\n        : manifest node in ascii
*         user\n          : user, no \n or \r allowed
*         time tz extra\n : date (time is int or float, timezone is int)
*                         : extra is metadatas, encoded and separated by '\0'
*                         : older versions ignore it
*         files\n\n       : files modified by the cset, no \n or \r allowed
*         (.*)            : comment (free text, ideally utf-8)
*
*         changelog v0 doesn't use extra
* </pre>
*
* Extracted from internals of HgChangelog (the code initially from inside RawChangeset)
*
* @author Artem Tikhomirov
* @author TMate Software Ltd.
*/
public final class ChangesetParser {
  private final EncodingHelper encHelper;
  // it's likely user names get repeated again and again throughout repository.
  private final Pool<String> usersPool;
  private final Pool<String> filesPool;
  private final CsetFactory factory;
 
  public ChangesetParser(SessionContext.Source sessionContex, CsetFactory csetFactory) {
    assert csetFactory != null;
    encHelper = Internals.buildFileNameEncodingHelper(sessionContex);
    usersPool = new Pool<String>();
    filesPool = new Pool<String>();
    factory = csetFactory;
  }
 
  public void dispose() {
    usersPool.clear();
    filesPool.clear();
  }

  public RawChangeset parse(DataAccess da) throws IOException, HgInvalidDataFormatException {
    byte[] data = da.byteArray();
    return parse(data);
  }
 
  public RawChangeset parse(byte[] data) throws HgInvalidDataFormatException {
    return init(data, 0, data.length);
  }

  private RawChangeset init(byte[] data, int offset, int length) throws HgInvalidDataFormatException {
    final int bufferEndIndex = offset + length;
    final byte lineBreak = (byte) '\n';
    int breakIndex1 = indexOf(data, lineBreak, offset, bufferEndIndex);
    if (breakIndex1 == -1) {
      throw new HgInvalidDataFormatException("Bad Changeset data");
    }
    Nodeid _nodeid = Nodeid.fromAscii(data, 0, breakIndex1);
    int breakIndex2 = indexOf(data, lineBreak, breakIndex1 + 1, bufferEndIndex);
    if (breakIndex2 == -1) {
      throw new HgInvalidDataFormatException("Bad Changeset data");
    }
    String _user;
    _user = encHelper.userFromChangeset(data, breakIndex1 + 1, breakIndex2 - breakIndex1 - 1);
    _user = usersPool.unify(_user);

    int breakIndex3 = indexOf(data, lineBreak, breakIndex2 + 1, bufferEndIndex);
    if (breakIndex3 == -1) {
      throw new HgInvalidDataFormatException("Bad Changeset data");
    }
    String _timeString = new String(data, breakIndex2 + 1, breakIndex3 - breakIndex2 - 1);
    int space1 = _timeString.indexOf(' ');
    if (space1 == -1) {
      throw new HgInvalidDataFormatException(String.format("Bad Changeset data: %s in [%d..%d]", "time string", breakIndex2+1, breakIndex3));
    }
    int space2 = _timeString.indexOf(' ', space1 + 1);
    if (space2 == -1) {
      space2 = _timeString.length();
    }
    long unixTime = Long.parseLong(_timeString.substring(0, space1));
    int _timezone = Integer.parseInt(_timeString.substring(space1 + 1, space2));
    // unixTime is local time, and timezone records difference of the local time to UTC.
    Date _time = new Date(unixTime * 1000);
    String _extras = space2 < _timeString.length() ? _timeString.substring(space2 + 1) : null;
    Map<String, String> _extrasMap = parseExtras(_extras);
    //
    int lastStart = breakIndex3 + 1;
    int breakIndex4 = indexOf(data, lineBreak, lastStart, bufferEndIndex);
    ArrayList<String> _files = null;
    if (breakIndex4 > lastStart) {
      // if breakIndex4 == lastStart, we already found \n\n and hence there are no files (e.g. merge revision)
      _files = new ArrayList<String>(5);
      while (breakIndex4 != -1 && breakIndex4 + 1 < bufferEndIndex) {
        String fname = encHelper.fileFromChangeset(data, lastStart, breakIndex4 - lastStart);
        _files.add(filesPool.unify(fname));
        lastStart = breakIndex4 + 1;
        if (data[breakIndex4 + 1] == lineBreak) {
          // found \n\n
          break;
        } else {
          breakIndex4 = indexOf(data, lineBreak, lastStart, bufferEndIndex);
        }
      }
      if (breakIndex4 == -1 || breakIndex4 >= bufferEndIndex) {
        throw new HgInvalidDataFormatException("Bad Changeset data");
      }
    } else {
      breakIndex4--;
    }
    String _comment = encHelper.commentFromChangeset(data, breakIndex4 + 2, bufferEndIndex - breakIndex4 - 2);
    RawChangeset target = factory.create(_nodeid, _user, _time, _timezone, _files, _comment, _extrasMap);
    return target;
  }

  private Map<String, String> parseExtras(String _extras) {
    final String extras_branch_key = "branch";
    _extras = _extras == null ? null : _extras.trim();
    if (_extras == null || _extras.length() == 0) {
      return Collections.singletonMap(extras_branch_key, HgRepository.DEFAULT_BRANCH_NAME);
    }
    Map<String, String> _extrasMap = new HashMap<String, String>();
    int lastIndex = 0;
    do {
      String pair;
      int sp = _extras.indexOf('\0', lastIndex);
      if (sp == -1) {
        sp = _extras.length();
      }
      if (sp > lastIndex) {
        pair = _extras.substring(lastIndex, sp);
        pair = decode(pair);
        int eq = pair.indexOf(':');
        _extrasMap.put(pair.substring(0, eq), pair.substring(eq + 1));
        lastIndex = sp + 1;
      }
    } while (lastIndex < _extras.length());
    if (!_extrasMap.containsKey(extras_branch_key)) {
      _extrasMap.put(extras_branch_key, HgRepository.DEFAULT_BRANCH_NAME);
    }
    return Collections.unmodifiableMap(_extrasMap);
  }

  private static int indexOf(byte[] src, byte what, int startOffset, int endIndex) {
    for (int i = startOffset; i < endIndex; i++) {
      if (src[i] == what) {
        return i;
      }
    }
    return -1;
  }
 
  private static String decode(String s) {
    if (s != null && s.indexOf('\\') != -1) {
      // TestAuxUtilities#testChangelogExtrasDecode
      return s.replace("\\\\", "\\").replace("\\n", "\n").replace("\\r", "\r").replace("\\0", "\00");
    }
    return s;
  }

  public interface CsetFactory {
    public RawChangeset create(Nodeid nodeid, String user, Date time, int timezone, List<String> files, String comment, Map<String, String> extrasMap);
  }
}
TOP

Related Classes of org.tmatesoft.hg.internal.ChangesetParser$CsetFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.