/*****************************************************************************
* Copyright (c) 2006-2009, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text of
* such license is available at www.eclipse.org.
*****************************************************************************/
package org.eclipse.buckminster.suffixdemangler;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.buckminster.cmdline.AbstractCommand;
import org.eclipse.buckminster.cmdline.Option;
import org.eclipse.buckminster.cmdline.OptionDescriptor;
import org.eclipse.buckminster.cmdline.OptionValueType;
import org.eclipse.buckminster.cmdline.SimpleErrorExitException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.osgi.util.NLS;
/**
* @author Thomas Hallgren
*
*/
public class SuffixDemangler extends AbstractCommand
{
public enum ShortSuffixType
{
SEQUENCE, TIMESTAMP
}
private static final DateFormat DefaultFormat;
private static final String DefaultPattern = "yyMMdd-HHmm"; //$NON-NLS-1$
private static final OptionDescriptor FORMAT_OPT = new OptionDescriptor('f', "format", //$NON-NLS-1$
OptionValueType.REQUIRED);
private static final OptionDescriptor IDENTIFIER_OPT = new OptionDescriptor('i', "identifier", //$NON-NLS-1$
OptionValueType.REQUIRED);
private static final OptionDescriptor QUALIFIER_OPT = new OptionDescriptor('q', "qualifier", //$NON-NLS-1$
OptionValueType.REQUIRED);
private static final OptionDescriptor STORAGE_FOLDER_OPT = new OptionDescriptor('s', "storage", //$NON-NLS-1$
OptionValueType.REQUIRED);
private static final OptionDescriptor TYPE_OPT = new OptionDescriptor('t', "type", //$NON-NLS-1$
OptionValueType.REQUIRED);
private static final OptionDescriptor VERSION_OPT = new OptionDescriptor('v', "version", //$NON-NLS-1$
OptionValueType.REQUIRED);
private static final OptionDescriptor WIDTH_OPT = new OptionDescriptor('w', "width", //$NON-NLS-1$
OptionValueType.REQUIRED);
private static final DateFormat ISO_8601Format;
private static final String ISO_8601Pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; //$NON-NLS-1$
// The line must be in the following format:
//
// <version> <ugly string> <timestamp> <sequence number>
//
// whitespace acts as a delimiter and is hence not allowed in any of the elements.
//
private static final Pattern s_linePattern = Pattern
.compile("^\\s*([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([0-9]+)\\s*$"); //$NON-NLS-1$
private static final Calendar UTC_Calendar;
static
{
UTC_Calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); //$NON-NLS-1$
ISO_8601Format = new SimpleDateFormat(ISO_8601Pattern);
ISO_8601Format.setCalendar(UTC_Calendar);
DefaultFormat = new SimpleDateFormat(DefaultPattern);
DefaultFormat.setCalendar(UTC_Calendar);
}
private static final String validateArgument(String argName, String arg)
{
int idx = 0;
if(arg != null)
{
arg = arg.trim();
idx = arg.length();
}
if(idx == 0)
throw new IllegalArgumentException(NLS.bind(Messages._0_cannotBeEmpty, argName));
while(--idx >= 0)
if(Character.isWhitespace(arg.charAt(idx)))
throw new IllegalArgumentException(NLS.bind(Messages._0_cannotContainWhitespace, argName));
return arg;
}
private DateFormat m_format;
private String m_identifier;
private String m_qualifier;
private File m_storageFolder;
private ShortSuffixType m_type = ShortSuffixType.TIMESTAMP;
private String m_version;
private int m_width = 4;
public String getTimestamp() throws IOException
{
if(m_storageFolder == null || m_identifier == null || m_version == null || m_qualifier == null)
throw new IllegalStateException(Messages.missingArguments);
String fileName = m_identifier + ".suffixmap"; //$NON-NLS-1$
File storageFile = new File(m_storageFolder, fileName);
RandomAccessFile raf = null;
FileLock rafLock = null;
try
{
raf = new RandomAccessFile(storageFile, "rw"); //$NON-NLS-1$
FileChannel rafChannel = raf.getChannel();
rafLock = rafChannel.lock();
int topSequence = 0;
int lineNumber = 0;
String line;
while((line = raf.readLine()) != null)
{
// Skip empty lines and lines that start with '#'
//
++lineNumber;
if(line.length() == 0 || line.charAt(0) == '#')
continue;
Matcher m = s_linePattern.matcher(line);
if(!m.matches())
continue;
String sequenceStr = m.group(4);
int sequence = Integer.parseInt(sequenceStr);
if(sequence > topSequence)
topSequence = sequence;
if(m.group(1).equals(m_version) && m.group(2).equals(m_qualifier))
{
Date timestamp;
try
{
timestamp = ISO_8601Format.parse(m.group(3));
}
catch(ParseException e)
{
throw new IOException(NLS.bind(Messages.file_0_hasCorruptTimestampAtLine_1, storageFile,
Integer.valueOf(lineNumber)));
}
return createReturnValue(timestamp, sequence);
}
}
// Nothing found. Write a new entry
//
topSequence++;
Date timestamp = new Date();
raf.writeBytes(m_version + ' ' + m_qualifier + ' ' + ISO_8601Format.format(timestamp) + ' ' + topSequence
+ System.getProperty("line.separator")); //$NON-NLS-1$
return createReturnValue(timestamp, topSequence);
}
finally
{
if(rafLock != null)
rafLock.release();
raf.close();
}
}
public void setID(String id)
{
m_identifier = validateArgument("ID", id); //$NON-NLS-1$
}
public void setQualifier(String qualifier)
{
m_qualifier = validateArgument("qualifier", qualifier); //$NON-NLS-1$
}
public void setStorageFolder(String folder)
{
File storageFolder = new File(folder);
if(!storageFolder.exists())
throw new IllegalArgumentException(NLS.bind(Messages.storageFolder_0_doesNotExist, storageFolder));
m_storageFolder = storageFolder;
}
public void setTimestampFormat(String formatString)
{
m_format = (formatString == null)
? null
: new SimpleDateFormat(formatString);
}
public void setType(ShortSuffixType type)
{
m_type = type;
}
public void setType(String type)
{
setType(ShortSuffixType.valueOf(validateArgument("type", type.toUpperCase()))); //$NON-NLS-1$;
}
public void setVersion(String version)
{
m_version = validateArgument("version", version); //$NON-NLS-1$
}
public void setWidth(int width)
{
if(width < 0)
throw new IllegalArgumentException(Messages.widthCannotBeNegative);
if(width > 10)
throw new IllegalArgumentException(Messages.widthCannotBeGreaterThen10);
m_width = width;
}
public void setWidth(String widthString)
{
if(widthString == null)
m_width = 4;
else
setWidth(Integer.parseInt(widthString));
}
@Override
protected void getOptionDescriptors(List<OptionDescriptor> appendHere) throws Exception
{
appendHere.add(FORMAT_OPT);
appendHere.add(IDENTIFIER_OPT);
appendHere.add(QUALIFIER_OPT);
appendHere.add(STORAGE_FOLDER_OPT);
appendHere.add(TYPE_OPT);
appendHere.add(VERSION_OPT);
appendHere.add(WIDTH_OPT);
}
@Override
protected void handleOption(Option option) throws Exception
{
if(option.is(FORMAT_OPT))
setTimestampFormat(option.getValue());
else if(option.is(IDENTIFIER_OPT))
setID(option.getValue());
else if(option.is(QUALIFIER_OPT))
setQualifier(option.getValue());
else if(option.is(STORAGE_FOLDER_OPT))
setStorageFolder(option.getValue());
else if(option.is(TYPE_OPT))
setType(option.getValue());
else if(option.is(VERSION_OPT))
setVersion(option.getValue());
else if(option.is(WIDTH_OPT))
setWidth(option.getValue());
};
@Override
protected void handleUnparsed(String[] unparsed) throws Exception
{
if(unparsed.length > 0)
throw new SimpleErrorExitException(Messages.tooManyArguments);
}
@Override
protected int run(IProgressMonitor monitor) throws Exception
{
System.out.println(getTimestamp());
return 0;
}
private String createReturnValue(Date timestamp, int topSequence)
{
if(m_type == ShortSuffixType.SEQUENCE)
{
String sqString = Integer.toString(topSequence);
if(m_width > 0)
{
int diff = m_width - sqString.length();
if(diff > 0)
{
StringBuilder sb = new StringBuilder(m_width);
while(--diff >= 0)
sb.append('0');
sb.append(sqString);
sqString = sb.toString();
}
}
return sqString;
}
if(m_format == null)
m_format = DefaultFormat;
return m_format.format(timestamp);
}
}