content: /*
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You under the Apache License, Version 2.0
   *  (the "License"); you may not use this file except in compliance with
   *  the License.  You may obtain a copy of the License at
   *
   *      https://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   *
   */
  package org.apache.ivy.core.module.id;
  
  import java.lang.ref.WeakReference;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.WeakHashMap;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import org.apache.ivy.core.IvyPatternHelper;
  
  /**
   * Identifies a module, without revision information
   *
   * @see <a href="package-summary.html">org.apache.ivy.core.module.id</a>
   */
  public class ModuleId implements Comparable<ModuleId> {
  
      static final String ENCODE_SEPARATOR = ":#@#:";
  
      private static final Map<ModuleId, WeakReference<ModuleId>> CACHE = new WeakHashMap<>();
  
      /**
       * Returns a ModuleId for the given organization and module name.
       *
       * @param org
       *            the module's organization, can be <code>null</code>
       * @param name
       *            the module's name, must not be <code>null</code>
       * @return a ModuleId instance
       */
      public static ModuleId newInstance(String org, String name) {
          return intern(new ModuleId(org, name));
      }
  
      /**
       * Returns an intern instance of a ModuleId equals to the given ModuleId if any, or the given
       * ModuleId.
       * <p>
       * This is useful to reduce the number of instances of ModuleId kept in memory, and thus reduce
       * memory footprint.
       * </p>
       *
       * @param moduleId
       *            the module id to return
       * @return a unit instance of the given module id.
       */
      public static ModuleId intern(ModuleId moduleId) {
          ModuleId r = null;
  
          synchronized (CACHE) {
              WeakReference<ModuleId> ref = CACHE.get(moduleId);
              if (ref != null) {
                  r = ref.get();
              }
              if (r == null) {
                  r = moduleId;
                  CACHE.put(r, new WeakReference<>(r));
              }
          }
  
          return r;
      }
  
      private String organisation;
  
      private String name;
  
      private int hash;
  
      private Map<String, String> attributes = new HashMap<>();
  
      /**
       * Constructor.
       *
       * @param organisation
       *            The organisation which creates the module.
       * @param name
       *            The name of the module.
       */
      public ModuleId(String organisation, String name) {
          if (name == null) {
              throw new IllegalArgumentException("null name not allowed");
          }
          this.organisation = organisation;
          this.name = name;
          attributes.put(IvyPatternHelper.ORGANISATION_KEY, organisation);
          attributes.put(IvyPatternHelper.MODULE_KEY, name);
      }
  
      /**
       * Returns the name of the module.
       *
       * @return The name of the module.
       */
      public String getName() {
          return name;
      }
  
      /**
       * Returns the name of the organisation.
       *
       * @return The name of the organisation.
       */
      public String getOrganisation() {
          return organisation;
      }
  
      @Override
      public boolean equals(Object obj) {
          if (!(obj instanceof ModuleId)) {
              return false;
          }
          ModuleId other = (ModuleId) obj;
          return (other.organisation == null)
                  ? organisation == null && other.name.equals(name)
                  : other.organisation.equals(organisation) && other.name.equals(name);
      }
  
      @Override
      public int hashCode() {
          if (hash == 0) {
              // CheckStyle:MagicNumber| OFF
              hash = 31;
              hash = hash * 13 + (organisation == null ? 0 : organisation.hashCode());
              hash = hash * 13 + name.hashCode();
              // CheckStyle:MagicNumber| ON
          }
          return hash;
      }
  
      @Override
      public String toString() {
          return organisation + "#" + name;
      }
  
      public int compareTo(ModuleId that) {
          int result = organisation.compareTo(that.organisation);
          if (result == 0) {
              result = name.compareTo(that.name);
          }
          return result;
      }
  
      /**
       * Returns the encoded String representing this ModuleId.
       *
       * @return The ModuleId encoded as String.
       */
      public String encodeToString() {
          return getOrganisation() + ENCODE_SEPARATOR + getName();
      }
  
      /**
       * Returns a Map of all attributes of this module id. The Map keys are attribute names as
       * Strings, and values are corresponding attribute values (as String too).
       *
       * @return A Map instance containing all the attributes and their values.
       */
      public Map<String, String> getAttributes() {
          return attributes;
      }
  
      /**
       * Returns a ModuleId
       *
       * @param encoded String
       * @return The new ModuleId.
       * @throws IllegalArgumentException
       *             If the given String could not be decoded.
       */
      public static ModuleId decode(String encoded) {
          String[] parts = encoded.split(ENCODE_SEPARATOR);
          if (parts.length != 2) {
              throw new IllegalArgumentException("badly encoded module id: '" + encoded + "'");
          }
          return new ModuleId(parts[0], parts[1]);
      }
  
      /**
       * Pattern to use to matched mid text representation.
       *
       * @see #parse(String)
       */
      public static final Pattern MID_PATTERN = Pattern.compile("("
              + ModuleRevisionId.STRICT_CHARS_PATTERN + "*)" + "#("
              + ModuleRevisionId.STRICT_CHARS_PATTERN + "+)");
  
      /**
       * Parses the module id text representation and returns it as a {@link ModuleId} instance.
       *
       * @param mid
       *            the module id text representation to parse
       * @return the ModuleId instance corresponding to the representation
       * @throws IllegalArgumentException
       *             if the given text representation cannot be parsed
       */
      public static ModuleId parse(String mid) {
          Matcher m = MID_PATTERN.matcher(mid);
          if (!m.matches()) {
              throw new IllegalArgumentException(
                      "module text representation do not match expected pattern." + " given mid='"
                              + mid + "' expected form=" + MID_PATTERN.pattern());
          }
  
          // CheckStyle:MagicNumber| OFF
          return newInstance(m.group(1), m.group(2));
          // CheckStyle:MagicNumber| ON
      }
  }
  