All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.ladutsko.isbn.ISBNFormat Maven / Gradle / Ivy

The newest version!
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2013-2024 George Ladutsko
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.github.ladutsko.isbn;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Matcher;

import org.isbn.prefix.ranges.model.Group;
import org.isbn.prefix.ranges.model.ISBNRangeMessage;
import org.isbn.prefix.ranges.model.Rule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.ladutsko.isbn.util.RangeMessageLoader;

/**
 * ISBN format
 *
 * @author George Ladutsko
 */
public class ISBNFormat {

  private static final Logger LOGGER = LoggerFactory.getLogger(ISBNFormat.class);

  public static final String HYPHEN_GROUP_SEPARATOR = "-";
  public static final String SPACE_GROUP_SEPARATOR = " ";

  private static final String RANGE_MESSAGE_RESOURCE_NAME = "RangeMessage.xml";

  private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
  private static Map> globalRangeMap;

  private String groupSeparator;

  public ISBNFormat() {
    this(HYPHEN_GROUP_SEPARATOR);
  }

  public ISBNFormat(final String groupSeparator) {
    this.groupSeparator = groupSeparator;
  }

  /**
   * @return groupSeparator
   */
  public String getGroupSeparator() {
    return groupSeparator;
  }

  /**
   * @param groupSeparator groupSeparator
   */
  public void setGroupSeparator(final String groupSeparator) {
    this.groupSeparator = groupSeparator;
  }

  /**
   * Format ISBN
   *
   * @param input character sequence  which contains ISBN
   * @return formatted ISBN or null if input is null
   * @throws ISBNException if something is wrong
   */
  public String format(final CharSequence input) throws ISBNException {
    return format(input, getGroupSeparator());
  }

  /**
   * Format ISBN
   *
   * @param input character sequence  which contains ISBN
   * @param groupSeparator groupSeparator
   * @return formatted ISBN or null if input is null
   * @throws ISBNException if something is wrong
   */
  public String format(final CharSequence input, final String groupSeparator) throws ISBNException {
    if (null == input)
      return null;

    Matcher m = ISBN.matcher(input);
    LOGGER.debug("Matcher: {}", m);

    assert 2 == m.groupCount() : "Unexpected groups count: " + m.groupCount();

    if (null != m.group(1)) {
      return format(ISBN.normalize(input), (null == groupSeparator ? HYPHEN_GROUP_SEPARATOR : groupSeparator), 3);
    } else {
      return format(ISBN.normalize(input), (null == groupSeparator ? HYPHEN_GROUP_SEPARATOR : groupSeparator), 0);
    }
  }

  protected String format(final String input, final String groupSeparator, final int beginIndex) {
    Map> rangeMap = getRangeMap();
    StringBuilder sb = new StringBuilder(17);
    if (0 < beginIndex)
      sb.append(input, 0, 3).append(groupSeparator);
    int start = beginIndex + 1;
    int end = beginIndex + 7;
    for (int i = start; end >= i; ++i) {
      String prefix = (0 == beginIndex ? ISBN.DEFAULT_PREFIX + input.substring(0, i) : input.substring(0, i)).intern();
      List rangeList = rangeMap.get(prefix);
      LOGGER.debug("Prefix {} contains {} range(s)", prefix, (null == rangeList ? 0 : rangeList.size()));
      if (null == rangeList) {
        continue;
      }
      sb.append(input, beginIndex, i).append(groupSeparator);
      if (rangeList.isEmpty()) {
        return sb.append(input, i, beginIndex + 9).append(groupSeparator).append(input.substring(beginIndex + 9)).toString();
      } else {
        for (Range range : rangeList) {
          String pubIdStr = input.substring(i, i + range.length);
          int pubId = Integer.parseInt(pubIdStr);
          if (range.min <= pubId && range.max >= pubId) {
            LOGGER.debug("Found publisher range {}-{}. Return formatted isbn.", range.min, range.max);
            return sb.append(pubIdStr).append(groupSeparator).append(input, i + range.length, beginIndex + 9).append(groupSeparator).append(input.substring(beginIndex + 9)).toString();
          }
        }
        break;
      }
    }

    LOGGER.debug("Not matched. Return simple formatted isbn.");

    sb.setLength(0);
    if (0 < beginIndex)
      sb.append(input, 0, 3).append(groupSeparator);
    return sb.append(input, beginIndex, beginIndex + 9).append(groupSeparator).append(input.substring(beginIndex + 9)).toString();
  }

  protected Map> getRangeMap() {
    LOGGER.trace("Start getRangeMap ...");
    rwl.readLock().lock();
    if (null == globalRangeMap) {
       // Must release read lock before acquiring write lock
       rwl.readLock().unlock();
       rwl.writeLock().lock();
       try {
         // Recheck state because another thread might have
         // acquired write lock and changed state before we did.
         if (null == globalRangeMap)
           initialize();
         // Downgrade by acquiring read lock before releasing write lock
         rwl.readLock().lock();
       } finally {
         rwl.writeLock().unlock(); // Unlock write, still hold read
       }
    }
    rwl.readLock().unlock();
    return globalRangeMap;
  }

  private static void initialize() {
    LOGGER.trace("Start initialize ...");
    try {
      ISBNRangeMessage isbnRangeMessage = new RangeMessageLoader()
        .load(ISBNFormat.class.getResource(RANGE_MESSAGE_RESOURCE_NAME).toString());
      Map> rangeMap = new TreeMap>();
      for (Group group : isbnRangeMessage.getRegistrationGroups().getGroup()) {
        List ruleList = group.getRules().getRule();
        List rangeList = new ArrayList(ruleList.size());
        for (Rule rule : ruleList) {
          int length = Integer.parseInt(rule.getLength());
          if (0 == length)
            continue;

          String rangeStr = rule.getRange();
          int p = rangeStr.indexOf('-');

          Range range = new Range();
          range.length = length;
          range.min = Integer.parseInt(rangeStr.substring(0, length));
          range.max = Integer.parseInt(rangeStr.substring(p+1, p+1+length));

          rangeList.add(range);
        }

        String prefix = group.getPrefix().replace("-", "").intern();
        LOGGER.debug("Put {} range(s) for prefix {}", rangeList.size(), prefix);
        rangeMap.put(prefix, rangeList);
      }
      globalRangeMap = Collections.unmodifiableMap(rangeMap);
    } catch (Exception e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  protected static class Range {
    protected int length;
    protected int min;
    protected int max;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy