/*
* ====================================================================
* Copyright (c) 2004-2012 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.admin;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.util.SVNCharsetInputStream;
import org.tmatesoft.svn.core.internal.util.SVNCharsetOutputStream;
import org.tmatesoft.svn.core.internal.util.SVNDate;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.internal.wc.IOExceptionWrapper;
import org.tmatesoft.svn.core.internal.wc.SVNAdminUtil;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileType;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @author TMate Software Ltd.
* @version 1.3
*/
public class SVNTranslator {
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
private static CodingErrorAction onMalformedInputAction = CodingErrorAction.REPORT;
private static CodingErrorAction onUnmappableCharacterAction = CodingErrorAction.REPORT;
public static String translateString(String str, byte[] eol, Map<String, byte[]> keywords, boolean repair, boolean expand) throws SVNException {
ByteArrayOutputStream bufferOS = new ByteArrayOutputStream();
OutputStream resultOS = null;
try {
resultOS = getTranslatingOutputStream(bufferOS, null, eol, repair, keywords, expand);
resultOS.write(str.getBytes());
} catch (IOException e) {
translationError(null, e);
} finally {
SVNFileUtil.closeFile(resultOS);
}
return new String(bufferOS.toByteArray());
}
public static void translate(SVNAdminArea adminArea, String name, String srcPath,
String dstPath, boolean expand) throws SVNException {
translate(adminArea, name, srcPath, dstPath, false, expand);
}
public static void translate(SVNAdminArea adminArea, String name, String srcPath,
String dstPath, boolean safelyEncode, boolean expand) throws SVNException {
translate(adminArea, name, adminArea.getFile(srcPath), adminArea.getFile(dstPath), null, safelyEncode, expand);
}
public static void translate(SVNAdminArea adminArea, String name, String srcPath,
String dstPath, String customEOLStyle, boolean expand) throws SVNException {
translate(adminArea, name, adminArea.getFile(srcPath), adminArea.getFile(dstPath), customEOLStyle, expand);
}
public static void translate(SVNAdminArea adminArea, String name, File src,
File dst, boolean expand) throws SVNException {
translate(adminArea, name, src, dst, null, expand);
}
public static void translate(SVNAdminArea adminArea, String name, File src,
File dst, boolean safelyEncode, boolean expand) throws SVNException {
translate(adminArea, name, src, dst, null,safelyEncode, expand);
}
public static void translate(SVNAdminArea adminArea, String name, File src,
File dst, String customEOLStyle, boolean expand) throws SVNException {
translate(adminArea, name, src, dst, customEOLStyle, false, expand);
}
public static void translate(SVNAdminArea adminArea, String name, File src,
File dst, String customEOLStyle, boolean safelyEncode, boolean expand) throws SVNException {
ISVNOptions options = adminArea.getWCAccess().getOptions();
SVNVersionedProperties props = adminArea.getProperties(name);
String keywords = props.getStringPropertyValue(SVNProperty.KEYWORDS);
String mimeType = props.getStringPropertyValue(SVNProperty.MIME_TYPE);
String charset = getCharset(props.getStringPropertyValue(SVNProperty.CHARSET), mimeType, adminArea.getFile(name).getPath(), options);
String eolStyle = null;
if (customEOLStyle != null) {
eolStyle = customEOLStyle;
} else {
eolStyle = props.getStringPropertyValue(SVNProperty.EOL_STYLE);
}
boolean special = props.getPropertyValue(SVNProperty.SPECIAL) != null;
Map<String, byte[]> keywordsMap = null;
byte[] eols;
if (keywords != null) {
if (expand) {
SVNEntry entry = adminArea.getVersionedEntry(name, true);
String url = entry.getURL();
String repositoryRoot = entry.getRepositoryRoot();
String author = entry.getAuthor();
String date = entry.getCommittedDate();
String rev = Long.toString(entry.getCommittedRevision());
keywordsMap = computeKeywords(keywords, url, repositoryRoot, author, date, rev, options);
} else {
keywordsMap = computeKeywords(keywords, null, null, null, null, null, null);
}
}
if (!expand) {
eols = getBaseEOL(eolStyle);
} else {
eols = getEOL(eolStyle, options);
}
if (expand && charset != null && safelyEncode) {
File tmp = SVNAdminUtil.createTmpFile(adminArea, name, ".tmp", true);
try {
translate(src, tmp, charset, eols, keywordsMap, special, false);
translate(tmp, dst, charset, eols, keywordsMap, special, true);
} finally {
SVNFileUtil.deleteFile(tmp);
}
} else {
translate(src, dst, charset, eols, keywordsMap, special, expand);
}
}
public static void translate(File src, File dst, String charset, byte[] eol, Map<String, byte[]> keywords, boolean special, boolean expand) throws SVNException {
if (src == null || dst == null) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.INCORRECT_PARAMS), SVNLogType.DEFAULT);
return;
}
if (src.equals(dst)) {
return;
}
if (special) {
if (SVNFileType.getType(dst) != SVNFileType.NONE) {
dst.delete();
}
if (!SVNFileUtil.symlinksSupported()) {
SVNFileUtil.copyFile(src, dst, true);
} else if (expand) {
// create symlink to target, and create it at dst
SVNFileUtil.createSymlink(dst, src);
} else {
SVNFileUtil.detranslateSymlink(src, dst);
}
return;
}
if ((charset == null || SVNProperty.isUTF8(charset)) && eol == null && (keywords == null || keywords.isEmpty())) {
// no expansion, fast copy.
SVNFileUtil.copyFile(src, dst, false, false);
return;
}
OutputStream os = SVNFileUtil.openFileForWriting(dst);
OutputStream tos = getTranslatingOutputStream(os, charset, eol, true, keywords, expand);
InputStream is = SVNFileUtil.openFileForReading(src, SVNLogType.WC);
try {
copy(is, tos);
} catch (IOException e) {
translationError(dst, e);
} finally {
SVNFileUtil.closeFile(tos);
SVNFileUtil.closeFile(os);
SVNFileUtil.closeFile(is);
}
}
public static InputStream getTranslatedStream(SVNAdminArea adminArea, String name, boolean translateToNormalForm, boolean repairEOL) throws SVNException {
ISVNOptions options = adminArea.getWCAccess().getOptions();
SVNVersionedProperties props = adminArea.getProperties(name);
String mimeType = props.getStringPropertyValue(SVNProperty.MIME_TYPE);
String charset = getCharset(props.getStringPropertyValue(SVNProperty.CHARSET), mimeType, adminArea.getFile(name).getPath(), options);
String eolStyle = props.getStringPropertyValue(SVNProperty.EOL_STYLE);
String keywords = props.getStringPropertyValue(SVNProperty.KEYWORDS);
boolean special = props.getPropertyValue(SVNProperty.SPECIAL) != null;
File src = adminArea.getFile(name);
if (special) {
if (!SVNFileUtil.symlinksSupported()) {
return SVNFileUtil.openFileForReading(src, SVNLogType.WC);
}
if (SVNFileType.getType(src) != SVNFileType.SYMLINK) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot detranslate symbolic link ''{0}''; file does not exist or not a symbolic link", src);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
String linkPath = SVNFileUtil.getSymlinkName(src);
if (linkPath == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot detranslate symbolic link ''{0}''; file does not exist or not a symbolic link", src);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
os.write("link ".getBytes("UTF-8"));
os.write(linkPath.getBytes("UTF-8"));
} catch (IOException e) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
} finally {
SVNFileUtil.closeFile(os);
}
return new ByteArrayInputStream(os.toByteArray());
}
boolean translationRequired = special || keywords != null || eolStyle != null || charset != null;
if (translationRequired) {
byte[] eol = getBaseEOL(eolStyle);
if (translateToNormalForm) {
if (eolStyle != null && eol == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_UNKNOWN_EOL);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
Map<String, byte[]> keywordsMap = computeKeywords(keywords, null, null, null, null, null, null);
boolean repair = (eolStyle != null && eol != null && !SVNProperty.EOL_STYLE_NATIVE.equals(eolStyle)) || repairEOL;
return getTranslatingInputStream(SVNFileUtil.openFileForReading(src, SVNLogType.WC), charset, eol, repair, keywordsMap, false);
}
SVNEntry entry = adminArea.getVersionedEntry(name, false);
String url = entry.getURL();
String repositoryRoot = entry.getRepositoryRoot();
String author = entry.getAuthor();
String date = entry.getCommittedDate();
String rev = Long.toString(entry.getCommittedRevision());
Map<String, byte[]> keywordsMap = computeKeywords(keywords, url, repositoryRoot, author, date, rev, options);
return getTranslatingInputStream(SVNFileUtil.openFileForReading(src, SVNLogType.WC), charset, eol, true, keywordsMap, true);
}
return SVNFileUtil.openFileForReading(src, SVNLogType.WC);
}
public static File getTranslatedFile(SVNAdminArea dir, String name, File src, boolean forceEOLRepair, boolean useGlobalTmp, boolean forceCopy, boolean toNormalFormat) throws SVNException {
ISVNOptions options = dir.getWCAccess().getOptions();
SVNVersionedProperties props = dir.getProperties(name);
String mimeType = props.getStringPropertyValue(SVNProperty.MIME_TYPE);
String charset = getCharset(props.getStringPropertyValue(SVNProperty.CHARSET), mimeType, dir.getFile(name).getPath(), options);
String eolStyle = props.getStringPropertyValue(SVNProperty.EOL_STYLE);
String keywords = props.getStringPropertyValue(SVNProperty.KEYWORDS);
boolean special = props.getPropertyValue(SVNProperty.SPECIAL) != null;
boolean needsTranslation = charset != null || eolStyle != null || keywords != null || special;
File result = null;
if (!needsTranslation && !forceCopy) {
result = src;
} else {
if (useGlobalTmp) {
result = SVNFileUtil.createTempFile("svndiff", ".tmp");
} else {
result = SVNAdminUtil.createTmpFile(dir, name, ".tmp", true);
}
if (toNormalFormat) {
translateToNormalForm(src, result, charset, eolStyle, forceEOLRepair, keywords, special);
} else {
SVNEntry entry = dir.getVersionedEntry(name, false);
String url = entry.getURL();
String repositoryRoot = entry.getRepositoryRoot();
String author = entry.getAuthor();
String date = entry.getCommittedDate();
String rev = Long.toString(entry.getCommittedRevision());
Map<String, byte[]> keywordsMap = computeKeywords(keywords, url, repositoryRoot, author, date, rev, options);
copyAndTranslate(src, result, charset, getEOL(eolStyle, options), keywordsMap, special, true, true);
}
}
return result;
}
public static File maybeUpdateTargetEOLs(SVNAdminArea dir, File target, SVNProperties propDiff) throws SVNException {
String eolStyle = null;
if (propDiff != null && propDiff.containsName(SVNProperty.EOL_STYLE) && propDiff.getStringValue(SVNProperty.EOL_STYLE) != null) {
eolStyle = propDiff.getStringValue(SVNProperty.EOL_STYLE);
ISVNOptions options = dir.getWCAccess().getOptions();
byte[] eol = getEOL(eolStyle, options);
File tmpFile = SVNAdminUtil.createTmpFile(dir);
copyAndTranslate(target, tmpFile, null, eol, null, false, false, true);
return tmpFile;
}
return target;
}
public static File detranslateWorkingCopy(SVNAdminArea dir, String name, SVNProperties propDiff, boolean force) throws SVNException {
SVNVersionedProperties props = dir.getProperties(name);
boolean isLocalBinary = SVNProperty.isBinaryMimeType(props.getStringPropertyValue(SVNProperty.MIME_TYPE));
String mimeType = null;
String charsetProp = null;
String eolStyle = null;
String keywords = null;
boolean isSpecial = false;
boolean isRemoteHasBinary = propDiff != null && propDiff.containsName(SVNProperty.MIME_TYPE);
boolean isRemoteBinaryRemoved = isRemoteHasBinary && !SVNProperty.isBinaryMimeType(propDiff.getStringValue(SVNProperty.MIME_TYPE));
boolean isRemoteBinary = isRemoteHasBinary && SVNProperty.isBinaryMimeType(propDiff.getStringValue(SVNProperty.MIME_TYPE));
if (!isLocalBinary && isRemoteBinary) {
isSpecial = props.getPropertyValue(SVNProperty.SPECIAL) != null;
keywords = props.getStringPropertyValue(SVNProperty.KEYWORDS);
charsetProp = props.getStringPropertyValue(SVNProperty.CHARSET);
mimeType = props.getStringPropertyValue(SVNProperty.MIME_TYPE);
} else if (!isLocalBinary || isRemoteBinaryRemoved) {
isSpecial = props.getPropertyValue(SVNProperty.SPECIAL) != null;
if (!isSpecial) {
if (propDiff != null && propDiff.getStringValue(SVNProperty.EOL_STYLE) != null) {
eolStyle = propDiff.getStringValue(SVNProperty.EOL_STYLE);
} else if (!isLocalBinary) {
eolStyle = props.getStringPropertyValue(SVNProperty.EOL_STYLE);
}
if (propDiff != null && propDiff.getStringValue(SVNProperty.CHARSET) != null) {
charsetProp = propDiff.getStringValue(SVNProperty.CHARSET);
mimeType = propDiff.getStringValue(SVNProperty.MIME_TYPE);
} else if (!isLocalBinary) {
charsetProp = props.getStringPropertyValue(SVNProperty.CHARSET);
mimeType = props.getStringPropertyValue(SVNProperty.MIME_TYPE);
}
if (!isLocalBinary) {
keywords = props.getStringPropertyValue(SVNProperty.KEYWORDS);
}
}
}
File detranslatedFile = null;
ISVNOptions options = dir.getWCAccess().getOptions();
String charset = getCharset(charsetProp, mimeType, dir.getFile(name).getPath(), options);
if (force || charset != null || keywords != null || eolStyle != null || isSpecial) {
File tmpFile = SVNAdminUtil.createTmpFile(dir);
translateToNormalForm(dir.getFile(name), tmpFile, charset, eolStyle, true, keywords, isSpecial);
detranslatedFile = tmpFile;
} else {
detranslatedFile = dir.getFile(name);
}
return detranslatedFile;
}
private static void translateToNormalForm(File source, File destination, String charset, String eolStyle, boolean alwaysRepairEOLs, String keywords, boolean isSpecial) throws SVNException {
byte[] eol = getBaseEOL(eolStyle);
if (eolStyle != null && eol == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_UNKNOWN_EOL);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
Map<String, byte[]> keywordsMap = computeKeywords(keywords, null, null, null, null, null, null);
boolean repair = (eolStyle != null && eol != null && !SVNProperty.EOL_STYLE_NATIVE.equals(eolStyle)) || alwaysRepairEOLs;
copyAndTranslate(source, destination, charset, eol, keywordsMap, isSpecial, false, repair);
}
public static void copyAndTranslate(File source, File destination, String charset, byte[] eol, Map<String, byte[]> keywords, boolean special, boolean expand, boolean repair) throws SVNException {
boolean isSpecialPath = false;
if (SVNFileUtil.symlinksSupported()) {
SVNFileType type = SVNFileType.getType(source);
isSpecialPath = type == SVNFileType.SYMLINK;
}
if (special || isSpecialPath) {
if (destination.exists()) {
destination.delete();
}
if (!SVNFileUtil.symlinksSupported()) {
SVNFileUtil.copyFile(source, destination, true);
} else if (expand) {
if (isSpecialPath) {
SVNFileUtil.createSymlink(destination, SVNFileUtil.getSymlinkName(source));
} else {
SVNFileUtil.createSymlink(destination, source);
}
} else {
SVNFileUtil.detranslateSymlink(source, destination);
}
return;
}
if ((charset == null || SVNProperty.isUTF8(charset)) && eol == null && (keywords == null || keywords.isEmpty())) {
// no expansion, fast copy.
SVNFileUtil.copyFile(source, destination, false);
return;
}
OutputStream dst = null;
InputStream src = null;
OutputStream translatingStream = null;
try {
dst = SVNFileUtil.openFileForWriting(destination);
src = SVNFileUtil.openFileForReading(source, SVNLogType.WC);
translatingStream = getTranslatingOutputStream(dst, charset, eol, repair, keywords, expand);
SVNTranslator.copy(src, translatingStream);
} catch (IOExceptionWrapper ew) {
if (ew.getOriginalException().getErrorMessage().getErrorCode() == SVNErrorCode.IO_INCONSISTENT_EOL) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_INCONSISTENT_EOL, "File ''{0}'' has inconsistent newlines", source);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
throw ew.getOriginalException();
} catch (IOException e) {
translationError(destination, e);
} finally {
try {
if (dst != null) {
try {
dst.flush();
} catch (IOException ioe) {
checkWrappedException(ioe, source);
}
}
if (translatingStream != null) {
try {
translatingStream.flush();
} catch (IOException ioe) {
checkWrappedException(ioe, source);
}
}
} finally {
SVNFileUtil.closeFile(src);
SVNFileUtil.closeFile(translatingStream);
SVNFileUtil.closeFile(dst);
}
}
}
private static void checkWrappedException(IOException ioe, File file) throws SVNException {
if(ioe instanceof IOExceptionWrapper) {
IOExceptionWrapper ew = (IOExceptionWrapper)ioe;
if (ew.getOriginalException().getErrorMessage().getErrorCode() == SVNErrorCode.IO_INCONSISTENT_EOL) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_INCONSISTENT_EOL, "File ''{0}'' has inconsistent newlines", file);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
throw ew.getOriginalException();
}
}
public static boolean checkNewLines(File file) {
if (file == null || !file.exists() || file.isDirectory()) {
return true;
}
InputStream is = null;
try {
is = SVNFileUtil.openFileForReading(file, SVNLogType.WC);
int r;
byte[] lastFoundEOL = null;
byte[] currentEOL = null;
while ((r = is.read()) >= 0) {
if (r == '\n') {
currentEOL = SVNProperty.EOL_LF_BYTES;
} else if (r == '\r') {
currentEOL = SVNProperty.EOL_CR_BYTES;
r = is.read();
if (r == '\n') {
currentEOL = SVNProperty.EOL_CRLF_BYTES;
}
}
if (lastFoundEOL == null) {
lastFoundEOL = currentEOL;
} else if (currentEOL != null && lastFoundEOL != currentEOL) {
return false;
}
}
} catch (IOException e) {
return false;
} catch (SVNException e) {
return false;
} finally {
SVNFileUtil.closeFile(is);
}
return true;
}
public static void copy(InputStream src, OutputStream dst) throws IOException {
byte[] buffer = new byte[8192];
while (true) {
int read = src.read(buffer);
if (read < 0) {
return;
} else if (read == 0) {
continue;
}
dst.write(buffer, 0, read);
}
}
public static OutputStream getTranslatingOutputStream(OutputStream out, String charset, byte[] eol, boolean repair, Map<String, byte[]> keywords, boolean expand) {
if (charset == null || SVNProperty.isUTF8(charset)) {
return new SVNTranslatorOutputStream(out, eol, repair, keywords, expand);
}
Charset cs = Charset.forName(charset);
byte[] lf = SVNProperty.EOL_LF_BYTES;
if (expand) {
if (eol != null) {
byte[] convertedEOL = convertEOL(lf, UTF8_CHARSET, cs);
boolean encodingConvertsEOL = !Arrays.equals(lf, convertedEOL);
if (Arrays.equals(convertedEOL, eol) && encodingConvertsEOL) {
out = new SVNCharsetOutputStream(out, UTF8_CHARSET, cs, getOnMalformedInputAction(), getOnUnmappableCharacterAction());
if (keywords != null) {
out = new SVNTranslatorOutputStream(out, null, false, keywords, expand);
}
return out;
}
}
out = new SVNCharsetOutputStream(out, UTF8_CHARSET, cs, getOnMalformedInputAction(), getOnUnmappableCharacterAction());
return new SVNTranslatorOutputStream(out, eol, repair, keywords, expand);
}
if (eol != null) {
byte[] convertedEOL = convertEOL(eol, cs, UTF8_CHARSET);
boolean encodingConvertsEOL = !Arrays.equals(eol, convertedEOL);
if (Arrays.equals(convertedEOL, lf) && encodingConvertsEOL) {
if (keywords != null) {
out = new SVNTranslatorOutputStream(out, null, false, keywords, expand);
}
return new SVNCharsetOutputStream(out, cs, UTF8_CHARSET, getOnMalformedInputAction(), getOnUnmappableCharacterAction());
}
}
out = new SVNTranslatorOutputStream(out, eol, repair, keywords, expand);
return new SVNCharsetOutputStream(out, cs, UTF8_CHARSET, getOnMalformedInputAction(), getOnUnmappableCharacterAction());
}
public static InputStream getTranslatingInputStream(InputStream in, String charset, byte[] eol, boolean repair, Map<String, byte[]> keywords, boolean expand) {
if (charset == null || SVNProperty.isUTF8(charset)) {
return new SVNTranslatorInputStream(in, eol, repair, keywords, expand);
}
final Charset cs = Charset.forName(charset);
byte[] lf = SVNProperty.EOL_LF_BYTES;
if (expand) {
if (eol != null) {
byte[] convertedEOL = convertEOL(lf, UTF8_CHARSET, cs);
boolean encodingConvertsEOL = !Arrays.equals(convertedEOL, lf);
if (Arrays.equals(eol, convertedEOL) && encodingConvertsEOL) {
if (keywords != null) {
in = new SVNTranslatorInputStream(in, null, false, keywords, expand);
}
return new SVNCharsetInputStream(in, UTF8_CHARSET, cs, getOnMalformedInputAction(), getOnUnmappableCharacterAction());
}
}
in = new SVNTranslatorInputStream(in, eol, repair, keywords, expand);
return new SVNCharsetInputStream(in, UTF8_CHARSET, cs, getOnMalformedInputAction(), getOnUnmappableCharacterAction());
}
if (eol != null) {
byte[] convertedEOL = convertEOL(eol, cs, UTF8_CHARSET);
boolean charsetConvertsEOL = !Arrays.equals(convertedEOL, eol);
if (Arrays.equals(lf, convertedEOL) && charsetConvertsEOL) {
in = new SVNCharsetInputStream(in, cs, UTF8_CHARSET, getOnMalformedInputAction(), getOnUnmappableCharacterAction());
if (keywords != null) {
in = new SVNTranslatorInputStream(in, null, false, keywords, expand);
}
return in;
}
}
in = new SVNCharsetInputStream(in, cs, UTF8_CHARSET, getOnMalformedInputAction(), getOnUnmappableCharacterAction());
return new SVNTranslatorInputStream(in, eol, repair, keywords, expand);
}
private static byte[] convertEOL(byte[] eol, Charset from, Charset to) {
ByteBuffer byteBuffer = ByteBuffer.wrap(eol);
CharBuffer charBuffer = from.decode(byteBuffer);
ByteBuffer resultBuffer = to.encode(charBuffer);
byte[] result = new byte[resultBuffer.limit()];
resultBuffer.get(result, 0, result.length);
return result;
}
public static Map<String, byte[]> computeKeywords(String keywords, String locationUrl, String repositoryRoot, String a, String d, String r, ISVNOptions options) {
if (keywords == null) {
return Collections.emptyMap();
}
boolean expand = locationUrl != null;
boolean expandCustomKeywords = expand; //if we want to prevent custom keywords from expanding, change this
Map<String, byte[]> map = new HashMap<String, byte[]>();
try {
SVNKeywordFormatter keywordFormatter = new SVNKeywordFormatter(expand, expandCustomKeywords, locationUrl == null ? null : SVNURL.parseURIEncoded(locationUrl), repositoryRoot == null ? null : SVNURL.parseURIEncoded(repositoryRoot), a, d, r, options);
for (StringTokenizer tokens = new StringTokenizer(keywords, " \t\n\b\r\f"); tokens.hasMoreTokens();) {
String token = tokens.nextToken();
String customFormat = null;
if (expandCustomKeywords) {
int pos = token.lastIndexOf('=');
if (pos >= 0) {
customFormat = token.substring(pos + 1);
token = token.substring(0, pos);
}
}
if (customFormat != null) {
byte[] customValue = keywordFormatter.format(customFormat);
map.put(token, customValue);
} else if ("LastChangedDate".equals(token) || "Date".equalsIgnoreCase(token)) {
byte[] date = keywordFormatter.format("%D");
map.put("LastChangedDate", date);
map.put("Date", date);
} else
if ("LastChangedRevision".equals(token) || "Revision".equals(token) || "Rev".equalsIgnoreCase(token)) {
byte[] revision = keywordFormatter.format("%r");
map.put("LastChangedRevision", revision);
map.put("Revision", revision);
map.put("Rev", revision);
} else if ("LastChangedBy".equals(token) || "Author".equalsIgnoreCase(token)) {
byte[] author = keywordFormatter.format("%a");
map.put("LastChangedBy", author);
map.put("Author", author);
} else if ("HeadURL".equals(token) || "URL".equalsIgnoreCase(token)) {
byte[] url = keywordFormatter.format("%u");
map.put("HeadURL", url);
map.put("URL", url);
} else if ("Id".equalsIgnoreCase(token)) {
byte[] id = keywordFormatter.format("%b %r %d %a");
map.put("Id", expand ? id : null);
} else if ("Header".equalsIgnoreCase(token)) {
byte[] header = keywordFormatter.format("%u %r %d %a");
map.put("Header", expand ? header : null);
}
}
} catch (SVNException e) {
//
}
return map;
}
public static byte[] getEOL(String eolStyle, ISVNOptions options) {
if (SVNProperty.EOL_STYLE_NATIVE.equals(eolStyle)) {
return options.getNativeEOL();
} else if (SVNProperty.EOL_STYLE_LF.equals(eolStyle)) {
return SVNProperty.EOL_LF_BYTES;
} else if (SVNProperty.EOL_STYLE_CR.equals(eolStyle)) {
return SVNProperty.EOL_CR_BYTES;
} else if (SVNProperty.EOL_STYLE_CRLF.equals(eolStyle)) {
return SVNProperty.EOL_CRLF_BYTES;
}
return null;
}
public static byte[] getBaseEOL(String eolStyle) {
if (SVNProperty.EOL_STYLE_NATIVE.equals(eolStyle)) {
return SVNProperty.EOL_LF_BYTES;
} else if (SVNProperty.EOL_STYLE_CR.equals(eolStyle)) {
return SVNProperty.EOL_CR_BYTES;
} else if (SVNProperty.EOL_STYLE_LF.equals(eolStyle)) {
return SVNProperty.EOL_LF_BYTES;
} else if (SVNProperty.EOL_STYLE_CRLF.equals(eolStyle)) {
return SVNProperty.EOL_CRLF_BYTES;
}
return null;
}
public static String getCharset(String charset, String mimeType, Object path, ISVNOptions options) throws SVNException {
if (charset == null) {
charset = getGlobalCharset(options, mimeType);
}
if (SVNProperty.NATIVE.equals(charset)) {
charset = options.getNativeCharset();
}
if (charset == null) {
return null;
}
boolean isSupported;
try {
isSupported = Charset.isSupported(charset);
} catch (IllegalCharsetNameException e) {
isSupported = false;
}
if (!isSupported) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR,
"Charset ''{0}'' is not supported on this computer; change svnkit:charset property value or remove that property for file ''{1}''",
new Object[]{charset, path}), SVNLogType.DEFAULT);
}
return charset;
}
private static String getGlobalCharset(ISVNOptions options, String mimeType) {
if (options instanceof DefaultSVNOptions && SVNProperty.isTextMimeType(mimeType)) {
DefaultSVNOptions defaults = (DefaultSVNOptions) options;
return defaults.getGlobalCharset();
}
return null;
}
public static void translationError(File path, IOException e) throws SVNException {
SVNErrorMessage error;
if (path != null) {
error = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Failed to translate ''{0}''", new Object[]{path});
} else {
error = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Translation failed");
}
Throwable cause = e;
if (e instanceof IOExceptionWrapper) {
IOExceptionWrapper wrapper = (IOExceptionWrapper) e;
SVNException wrappedException = wrapper.getOriginalException();
if (wrappedException != null) {
error.setChildErrorMessage(wrappedException.getErrorMessage());
cause = wrappedException;
}
}
SVNErrorManager.error(error, cause, SVNLogType.DEFAULT);
}
public static synchronized CodingErrorAction getOnMalformedInputAction() {
return onMalformedInputAction;
}
public static synchronized CodingErrorAction getOnUnmappableCharacterAction() {
return onUnmappableCharacterAction;
}
public static synchronized void setEncoderActions(CodingErrorAction onMalformedInput, CodingErrorAction onUnmappableCharacter) {
onMalformedInputAction = onMalformedInput;
onUnmappableCharacterAction = onUnmappableCharacter;
}
private static class SVNKeywordFormatter {
private boolean expand;
private boolean expandCustomKeywords;
private String authorString;
private String dateString;
private String revisionString;
private ISVNOptions options;
private byte[] date;
private byte[] idDate;
private byte[] url;
private byte[] baseUrl;
private byte[] repositoryRoot;
private byte[] reposRelPath;
private byte[] rev;
private byte[] author;
private byte[] name;
private byte[] id;
private byte[] header;
private Date javaDate;
private SVNURL locationUrl;
private SVNURL repositoryRootUrl;
private SVNKeywordFormatter(boolean expand, boolean expandCustomKeywords, SVNURL locationUrl, SVNURL repositoryRootUrl, String authorString, String dateString, String revisionString, ISVNOptions options) {
this.expand = expand;
this.expandCustomKeywords = expandCustomKeywords;
this.authorString = authorString;
this.dateString = dateString;
this.revisionString = revisionString;
this.options = options;
this.locationUrl = locationUrl;
this.repositoryRootUrl = repositoryRootUrl;
date = null;
idDate = null;
url = null;
rev = null;
author = null;
name = null;
id = null;
header = null;
}
private byte[] format(String format) throws SVNException {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
int pos;
while (true) {
pos = 0;
while (pos < format.length() && format.charAt(pos) != '%') {
pos++;
}
byteArrayOutputStream.write(format.substring(0, pos).getBytes("UTF-8"));
if (pos == format.length()) {
break;
}
if (pos + 1 == format.length()) {
byteArrayOutputStream.write(format.charAt(pos));
pos--;
} else {
switch (format.charAt(pos + 1)) {
case 'a':
byte[] author = getAuthor();
if (author != null) {
byteArrayOutputStream.write(author);
}
break;
case 'b':
byte[] baseUrl = getName();
if (baseUrl != null) {
byteArrayOutputStream.write(baseUrl);
}
break;
case 'd':
byte[] idDate = getIdDate();
if (idDate != null) {
byteArrayOutputStream.write(idDate);
}
break;
case 'D':
byte[] date = getDate();
if (date != null) {
byteArrayOutputStream.write(date);
}
break;
case 'P':
byte[] reposRelPath = getReposRelPath();
if (reposRelPath != null) {
byteArrayOutputStream.write(reposRelPath);
}
break;
case 'R':
byte[] repositoryRoot = getRepositoryRoot();
if (repositoryRoot != null) {
byteArrayOutputStream.write(repositoryRoot);
}
break;
case 'r':
final byte[] revision = getRevision();
if (revision != null) {
byteArrayOutputStream.write(revision);
}
break;
case 'u':
final byte[] url = getUrl();
if (url != null) {
byteArrayOutputStream.write(url);
}
break;
case '_':
byteArrayOutputStream.write(' ');
break;
case '%':
byteArrayOutputStream.write('%');
break;
case 'H':
byteArrayOutputStream.write(format("%P%_%r%_%d%_%a"));
break;
case 'I':
byteArrayOutputStream.write(format("%b%_%r%_%d%_%a"));
break;
default:
byteArrayOutputStream.write(format.charAt(pos));
byteArrayOutputStream.write(format.charAt(pos + 1));
break;
}
}
format = format.substring(pos + 2);
}
return byteArrayOutputStream.toByteArray();
} catch (UnsupportedEncodingException e) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e);
SVNErrorManager.error(errorMessage, SVNLogType.CLIENT);
} catch (IOException e) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e);
SVNErrorManager.error(errorMessage, SVNLogType.CLIENT);
} finally {
SVNFileUtil.closeFile(byteArrayOutputStream);
}
return null;
}
private Date getJavaDate() {
if (javaDate == null) {
javaDate = dateString == null ? null : SVNDate.parseDate(dateString);
}
return javaDate;
}
private byte[] getAuthor() throws UnsupportedEncodingException {
if (author == null) {
author = expand ? (authorString == null ? new byte[0] : authorString.getBytes("UTF-8")) : author;
}
return author;
}
private byte[] getDate() throws UnsupportedEncodingException {
if (date == null) {
date = expand ? SVNDate.formatHumanDate(getJavaDate(), options).getBytes("UTF-8") : date;
}
return date;
}
private byte[] getRevision() throws UnsupportedEncodingException {
if (rev == null) {
rev = expand ? revisionString.getBytes("UTF-8") : rev;
}
return rev;
}
private byte[] getUrl() throws UnsupportedEncodingException {
if (url == null) {
if (locationUrl != null) {
url = expand ? locationUrl.toString().getBytes("UTF-8") : url;
}
}
return url;
}
private byte[] getBaseUrl() throws UnsupportedEncodingException, SVNException {
if (baseUrl == null) {
if (locationUrl != null) {
baseUrl = locationUrl.removePathTail().toString().getBytes("UTF-8");
}
}
return baseUrl;
}
private byte[] getRepositoryRoot() throws UnsupportedEncodingException {
if (repositoryRoot == null) {
if (repositoryRootUrl != null) {
repositoryRoot = repositoryRootUrl.toString().getBytes("UTF-8");
}
}
return repositoryRoot;
}
private byte[] getReposRelPath() throws UnsupportedEncodingException {
if (reposRelPath == null) {
if (repositoryRootUrl != null && locationUrl != null) {
reposRelPath = SVNPathUtil.getRelativePath(repositoryRootUrl.toDecodedString(), locationUrl.toDecodedString()).getBytes("UTF-8");
}
}
return reposRelPath;
}
private byte[] getName() throws UnsupportedEncodingException {
if (name == null) {
if (locationUrl != null) {
name = SVNEncodingUtil.uriDecode(SVNPathUtil.tail(locationUrl.toDecodedString())).getBytes("UTF-8");
}
}
return name;
}
private byte[] getIdDate() throws UnsupportedEncodingException {
if (idDate == null) {
idDate = SVNDate.formatShortDate(getJavaDate()).getBytes("UTF-8");
}
return idDate;
}
private byte[] getId() throws IOException {
if (id == null) {
if (expand) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(getName());
bos.write(' ');
bos.write(getRevision());
bos.write(' ');
bos.write(getIdDate());
bos.write(' ');
bos.write(getAuthor());
bos.close();
id = bos.toByteArray();
}
}
return id;
}
private byte[] getHeader() throws IOException {
if (header == null) {
if (expand) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(getUrl());
bos.write(' ');
bos.write(getRevision());
bos.write(' ');
bos.write(getIdDate());
bos.write(' ');
bos.write(getAuthor());
bos.close();
header = bos.toByteArray();
}
}
return header;
}
}
}