Allow ArchiveCommand.registerFormat to be called twice
This should make it possible for the gitiles plugin to register its archive formats after gerrit has already registered them. Signed-off-by: Jonathan Nieder <jrn@google.com> Change-Id: Icb80a446e583961a7278b707d572d6fe456c372c
This commit is contained in:
parent
257546a08e
commit
d5110c32f9
|
@ -66,7 +66,6 @@ private static final void register(String name, ArchiveCommand.Format<?> fmt) {
|
||||||
* Register all included archive formats so they can be used
|
* Register all included archive formats so they can be used
|
||||||
* as arguments to the ArchiveCommand.setFormat() method.
|
* as arguments to the ArchiveCommand.setFormat() method.
|
||||||
*
|
*
|
||||||
* Should not be called twice without a call to stop() in between.
|
|
||||||
* Not thread-safe.
|
* Not thread-safe.
|
||||||
*/
|
*/
|
||||||
public static void registerAll() {
|
public static void registerAll() {
|
||||||
|
|
|
@ -12,7 +12,7 @@ anExceptionOccurredWhileTryingToAddTheIdOfHEAD=An exception occurred while tryin
|
||||||
anSSHSessionHasBeenAlreadyCreated=An SSH session has been already created
|
anSSHSessionHasBeenAlreadyCreated=An SSH session has been already created
|
||||||
applyingCommit=Applying {0}
|
applyingCommit=Applying {0}
|
||||||
archiveFormatAlreadyAbsent=Archive format already absent: {0}
|
archiveFormatAlreadyAbsent=Archive format already absent: {0}
|
||||||
archiveFormatAlreadyRegistered=Archive format already registered: {0}
|
archiveFormatAlreadyRegistered=Archive format already registered with different implementation: {0}
|
||||||
argumentIsNotAValidCommentString=Invalid comment: {0}
|
argumentIsNotAValidCommentString=Invalid comment: {0}
|
||||||
atLeastOnePathIsRequired=At least one path is required.
|
atLeastOnePathIsRequired=At least one path is required.
|
||||||
atLeastOnePatternIsRequired=At least one pattern is required.
|
atLeastOnePatternIsRequired=At least one pattern is required.
|
||||||
|
|
|
@ -189,65 +189,135 @@ public String getFormat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class FormatEntry {
|
||||||
|
final Format<?> format;
|
||||||
|
/** Number of times this format has been registered. */
|
||||||
|
final int refcnt;
|
||||||
|
|
||||||
|
public FormatEntry(Format<?> format, int refcnt) {
|
||||||
|
if (format == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
this.format = format;
|
||||||
|
this.refcnt = refcnt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Available archival formats (corresponding to values for
|
* Available archival formats (corresponding to values for
|
||||||
* the --format= option)
|
* the --format= option)
|
||||||
*/
|
*/
|
||||||
private static final ConcurrentMap<String, Format<?>> formats =
|
private static final ConcurrentMap<String, FormatEntry> formats =
|
||||||
new ConcurrentHashMap<String, Format<?>>();
|
new ConcurrentHashMap<String, FormatEntry>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the entry for a key only if currently mapped to a given
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param map a map
|
||||||
|
* @param key key with which the specified value is associated
|
||||||
|
* @param oldValue expected value for the key (null if should be absent).
|
||||||
|
* @param newValue value to be associated with the key (null to remove).
|
||||||
|
* @return true if the value was replaced
|
||||||
|
*/
|
||||||
|
private static <K, V> boolean replace(ConcurrentMap<K, V> map,
|
||||||
|
K key, V oldValue, V newValue) {
|
||||||
|
if (oldValue == null && newValue == null) // Nothing to do.
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (oldValue == null)
|
||||||
|
return map.putIfAbsent(key, newValue) == null;
|
||||||
|
else if (newValue == null)
|
||||||
|
return map.remove(key, oldValue);
|
||||||
|
else
|
||||||
|
return map.replace(key, oldValue, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds support for an additional archival format. To avoid
|
* Adds support for an additional archival format. To avoid
|
||||||
* unnecessary dependencies, ArchiveCommand does not have support
|
* unnecessary dependencies, ArchiveCommand does not have support
|
||||||
* for any formats built in; use this function to add them.
|
* for any formats built in; use this function to add them.
|
||||||
*
|
* <p>
|
||||||
* OSGi plugins providing formats should call this function at
|
* OSGi plugins providing formats should call this function at
|
||||||
* bundle activation time.
|
* bundle activation time.
|
||||||
|
* <p>
|
||||||
|
* It is okay to register the same archive format with the same
|
||||||
|
* name multiple times, but don't forget to unregister it that
|
||||||
|
* same number of times, too.
|
||||||
|
* <p>
|
||||||
|
* Registering multiple formats with different names and the
|
||||||
|
* same or overlapping suffixes results in undefined behavior.
|
||||||
|
* TODO: check that suffixes don't overlap.
|
||||||
*
|
*
|
||||||
* @param name name of a format (e.g., "tar" or "zip").
|
* @param name name of a format (e.g., "tar" or "zip").
|
||||||
* @param fmt archiver for that format
|
* @param fmt archiver for that format
|
||||||
* @throws JGitInternalException
|
* @throws JGitInternalException
|
||||||
* An archival format with that name was already registered.
|
* A different archival format with that name was
|
||||||
|
* already registered.
|
||||||
*/
|
*/
|
||||||
public static void registerFormat(String name, Format<?> fmt) {
|
public static void registerFormat(String name, Format<?> fmt) {
|
||||||
// TODO(jrn): Check that suffixes don't overlap.
|
if (fmt == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
|
||||||
if (formats.putIfAbsent(name, fmt) != null)
|
FormatEntry old, entry;
|
||||||
throw new JGitInternalException(MessageFormat.format(
|
do {
|
||||||
JGitText.get().archiveFormatAlreadyRegistered,
|
old = formats.get(name);
|
||||||
name));
|
if (old == null) {
|
||||||
|
entry = new FormatEntry(fmt, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!old.format.equals(fmt))
|
||||||
|
throw new JGitInternalException(MessageFormat.format(
|
||||||
|
JGitText.get().archiveFormatAlreadyRegistered,
|
||||||
|
name));
|
||||||
|
entry = new FormatEntry(old.format, old.refcnt + 1);
|
||||||
|
} while (!replace(formats, name, old, entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes support for an archival format so its Format can be
|
* Marks support for an archival format as no longer needed so its
|
||||||
* garbage collected.
|
* Format can be garbage collected if no one else is using it either.
|
||||||
|
* <p>
|
||||||
|
* In other words, this decrements the reference count for an
|
||||||
|
* archival format. If the reference count becomes zero, removes
|
||||||
|
* support for that format.
|
||||||
*
|
*
|
||||||
* @param name name of format (e.g., "tar" or "zip").
|
* @param name name of format (e.g., "tar" or "zip").
|
||||||
* @throws JGitInternalException
|
* @throws JGitInternalException
|
||||||
* No such archival format was registered.
|
* No such archival format was registered.
|
||||||
*/
|
*/
|
||||||
public static void unregisterFormat(String name) {
|
public static void unregisterFormat(String name) {
|
||||||
if (formats.remove(name) == null)
|
FormatEntry old, entry;
|
||||||
throw new JGitInternalException(MessageFormat.format(
|
do {
|
||||||
JGitText.get().archiveFormatAlreadyAbsent,
|
old = formats.get(name);
|
||||||
name));
|
if (old == null)
|
||||||
|
throw new JGitInternalException(MessageFormat.format(
|
||||||
|
JGitText.get().archiveFormatAlreadyAbsent,
|
||||||
|
name));
|
||||||
|
if (old.refcnt == 1) {
|
||||||
|
entry = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry = new FormatEntry(old.format, old.refcnt - 1);
|
||||||
|
} while (!replace(formats, name, old, entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Format<?> formatBySuffix(String filenameSuffix)
|
private static Format<?> formatBySuffix(String filenameSuffix)
|
||||||
throws UnsupportedFormatException {
|
throws UnsupportedFormatException {
|
||||||
if (filenameSuffix != null)
|
if (filenameSuffix != null)
|
||||||
for (Format<?> fmt : formats.values())
|
for (FormatEntry entry : formats.values()) {
|
||||||
|
Format<?> fmt = entry.format;
|
||||||
for (String sfx : fmt.suffixes())
|
for (String sfx : fmt.suffixes())
|
||||||
if (filenameSuffix.endsWith(sfx))
|
if (filenameSuffix.endsWith(sfx))
|
||||||
return fmt;
|
return fmt;
|
||||||
|
}
|
||||||
return lookupFormat("tar"); //$NON-NLS-1$
|
return lookupFormat("tar"); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Format<?> lookupFormat(String formatName) throws UnsupportedFormatException {
|
private static Format<?> lookupFormat(String formatName) throws UnsupportedFormatException {
|
||||||
Format<?> fmt = formats.get(formatName);
|
FormatEntry entry = formats.get(formatName);
|
||||||
if (fmt == null)
|
if (entry == null)
|
||||||
throw new UnsupportedFormatException(formatName);
|
throw new UnsupportedFormatException(formatName);
|
||||||
return fmt;
|
return entry.format;
|
||||||
}
|
}
|
||||||
|
|
||||||
private OutputStream out;
|
private OutputStream out;
|
||||||
|
|
Loading…
Reference in New Issue