]> source.dussan.org Git - jgit.git/commitdiff
Allow ArchiveCommand.registerFormat to be called twice 87/24887/5
authorJonathan Nieder <jrn@google.com>
Wed, 16 Apr 2014 18:52:02 +0000 (11:52 -0700)
committerJonathan Nieder <jrn@google.com>
Wed, 16 Apr 2014 18:52:02 +0000 (11:52 -0700)
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

org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ArchiveFormats.java
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java

index 65466a99494b3708bdcadcebd1d3ec7fa6378bff..1be126aa8a6ade586110832e90468bd4809dd44e 100644 (file)
@@ -66,7 +66,6 @@ public class ArchiveFormats {
         * Register all included archive formats so they can be used
         * as arguments to the ArchiveCommand.setFormat() method.
         *
-        * Should not be called twice without a call to stop() in between.
         * Not thread-safe.
         */
        public static void registerAll() {
index 3bc682be71c6e28084c81b201830b0a1b04a2e3a..bb95fa85fda16f8d16b3aaac62948b28270cce0d 100644 (file)
@@ -12,7 +12,7 @@ anExceptionOccurredWhileTryingToAddTheIdOfHEAD=An exception occurred while tryin
 anSSHSessionHasBeenAlreadyCreated=An SSH session has been already created
 applyingCommit=Applying {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}
 atLeastOnePathIsRequired=At least one path is required.
 atLeastOnePatternIsRequired=At least one pattern is required.
index 1bafb5efc6ce22db17a698b9215a225e67a6ba3b..70ab73015f7d9e9eceebdbd7078934e37c867155 100644 (file)
@@ -189,65 +189,135 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
                }
        }
 
+       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
         * the --format= option)
         */
-       private static final ConcurrentMap<String, Format<?>> formats =
-                       new ConcurrentHashMap<String, Format<?>>();
+       private static final ConcurrentMap<String, FormatEntry> formats =
+                       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
         * unnecessary dependencies, ArchiveCommand does not have support
         * for any formats built in; use this function to add them.
-        *
+        * <p>
         * OSGi plugins providing formats should call this function at
         * 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 fmt archiver for that format
         * @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) {
-               // TODO(jrn): Check that suffixes don't overlap.
-
-               if (formats.putIfAbsent(name, fmt) != null)
-                       throw new JGitInternalException(MessageFormat.format(
-                                       JGitText.get().archiveFormatAlreadyRegistered,
-                                       name));
+               if (fmt == null)
+                       throw new NullPointerException();
+
+               FormatEntry old, entry;
+               do {
+                       old = formats.get(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
-        * garbage collected.
+        * Marks support for an archival format as no longer needed so its
+        * 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").
         * @throws JGitInternalException
         *              No such archival format was registered.
         */
        public static void unregisterFormat(String name) {
-               if (formats.remove(name) == null)
-                       throw new JGitInternalException(MessageFormat.format(
-                                       JGitText.get().archiveFormatAlreadyAbsent,
-                                       name));
+               FormatEntry old, entry;
+               do {
+                       old = formats.get(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)
                        throws UnsupportedFormatException {
                if (filenameSuffix != null)
-                       for (Format<?> fmt : formats.values())
+                       for (FormatEntry entry : formats.values()) {
+                               Format<?> fmt = entry.format;
                                for (String sfx : fmt.suffixes())
                                        if (filenameSuffix.endsWith(sfx))
                                                return fmt;
+                       }
                return lookupFormat("tar"); //$NON-NLS-1$
        }
 
        private static Format<?> lookupFormat(String formatName) throws UnsupportedFormatException {
-               Format<?> fmt = formats.get(formatName);
-               if (fmt == null)
+               FormatEntry entry = formats.get(formatName);
+               if (entry == null)
                        throw new UnsupportedFormatException(formatName);
-               return fmt;
+               return entry.format;
        }
 
        private OutputStream out;