AlphanumericGenerator.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. /*
  18.  * Modificato da Link.it (https://link.it) per supportare le seguenti funzionalità:
  19.  * - Generazione ID all'interno delle interfacce di OpenSPCoop2
  20.  * - Gestione caratteri massimi per numeri e cifre
  21.  * - Possibilità di utilizzare lowerCase e/o upperCase
  22.  *
  23.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  24.  */
  25. package org.openspcoop2.utils.id.apache.serial;

  26. import org.openspcoop2.utils.UtilsException;
  27. import org.openspcoop2.utils.id.apache.AbstractStringIdentifierGenerator;
  28. import org.openspcoop2.utils.regexp.RegularExpressionEngine;

  29. import java.io.Serializable;

  30. /**
  31.  * <code>AlphanumericGenerator</code> is an identifier generator
  32.  * that generates an incrementing number in base 36 as a String
  33.  * object.
  34.  *
  35.  * <p>All generated ids have the same length (padding with 0's on the left),
  36.  * which is determined by the <code>size</code> parameter passed to the constructor.<p>
  37.  *
  38.  * <p>The <code>wrap</code> property determines whether or not the sequence wraps
  39.  * when it reaches the largest value that can be represented in <code>size</code>
  40.  * base 36 digits. If <code>wrap</code> is false and the the maximum representable
  41.  * value is exceeded, an IllegalStateException is thrown</p>.
  42.  *
  43.  * Author of the original commons apache code:
  44.  * @author Commons-Id team
  45.  * @version $Id$
  46.  *
  47.  * Authors of the Link.it modification to the code:
  48.  * @author $Author$
  49.  * @version $Rev$, $Date$
  50.  */
  51. public class AlphanumericGenerator extends AbstractStringIdentifierGenerator implements Serializable {

  52.     /**
  53.      * <code>serialVersionUID</code> is the serializable UID for the binary version of the class.
  54.      */
  55.     private static final long serialVersionUID = 20060120L;

  56.     /**
  57.      * Should the counter wrap.
  58.      */
  59.     private boolean wrapping = true;

  60.     /**
  61.      * The counter.
  62.      */
  63.     private char[] count = null;


  64.     private char START_CHAR = 'a';
  65.     public void setStartChar(char startChar) throws UtilsException{
  66.         try{
  67.             if(!RegularExpressionEngine.isMatch((startChar+""),"^[a-z]$")){
  68.                 throw new UtilsException("Deve essere fornito una lettera [a-z]");
  69.             }
  70.         }catch(Exception e){
  71.             throw new UtilsException(e.getMessage(),e);
  72.         }
  73.         if(startChar>this.END_CHAR){
  74.             throw new UtilsException("Deve essere fornito una lettera di posizione minore dell'attuale carattere di fine: ["+this.END_CHAR+"]");
  75.         }
  76.         this.START_CHAR = startChar;
  77.     }
  78.    
  79.     private char END_CHAR = 'z';
  80.     public void setEndChar(char endChar) throws UtilsException{
  81.         try{
  82.             if(!RegularExpressionEngine.isMatch((endChar+""),"^[a-z]$")){
  83.                 throw new UtilsException("Deve essere fornito una lettera [a-z]");
  84.             }
  85.         }catch(Exception e){
  86.             throw new UtilsException(e.getMessage(),e);
  87.         }
  88.         if(endChar<this.START_CHAR){
  89.             throw new UtilsException("Deve essere fornito una lettera di posizione maggiore dell'attuale carattere di inizio: ["+this.START_CHAR+"]");
  90.         }
  91.         this.END_CHAR = endChar;
  92.     }

  93.     private boolean upperChar = true;
  94.     public void setUpperChar(boolean upperChar) throws UtilsException {
  95.         //System.out.println("SET UPPER ["+upperChar+"]");
  96.         if(!upperChar && !this.lowerChar){
  97.             throw new UtilsException("Almeno una combinazione tra maiuscolo e minuscolo deve essere permessa");
  98.         }
  99.         this.upperChar = upperChar;
  100.     }

  101.     private boolean lowerChar = true;
  102.     public void setLowerChar(boolean lowerChar) throws UtilsException {
  103.         //System.out.println("SET LOWER ["+lowerChar+"]");
  104.         if(!lowerChar && !this.upperChar){
  105.             throw new UtilsException("Almeno una combinazione tra maiuscolo e minuscolo deve essere permessa");
  106.         }
  107.         this.lowerChar = lowerChar;
  108.     }

  109.    
  110.     private char START_DIGIT = '0';
  111.     private boolean nonAncoraUtilizzato = true;
  112.     public void setStartDigit(char startDigit) throws UtilsException{
  113. //      if(this.nonAncoraUtilizzato==false){
  114. //          throw new UtilsException("Deve essere indicato prima di generare un identificativo");
  115. //      }
  116.         if(Character.isDigit(startDigit)==false){
  117.             throw new UtilsException("Deve essere fornito un numero");
  118.         }
  119.         if(startDigit>this.END_DIGIT){
  120.             throw new UtilsException("Deve essere fornito un numero di posizione minore dell'attuale numero di fine: ["+this.END_DIGIT+"]");
  121.         }
  122.         this.START_DIGIT = startDigit;
  123.         if(this.nonAncoraUtilizzato){
  124.             for (int i = 0; i < this.count.length; i++) {
  125.                 this.count[i] = this.START_DIGIT;  // zero
  126.             }
  127.         }
  128.     }
  129.    
  130.     private char END_DIGIT = '9';
  131.     public void setEndDigit(char endDigit) throws UtilsException{
  132.         if(Character.isDigit(endDigit)==false){
  133.             throw new UtilsException("Deve essere fornito un numero");
  134.         }
  135.         if(endDigit<this.START_DIGIT){
  136.             throw new UtilsException("Deve essere fornito un numero di posizione maggiore dell'attuale numero di inizio: ["+this.START_DIGIT+"]");
  137.         }
  138.         this.END_DIGIT = endDigit;
  139.     }

  140.    
  141.    
  142.    
  143.    
  144.     public void setStartEndChar(char startChar,char endChar) throws UtilsException{
  145.         if(startChar>endChar){
  146.             throw new UtilsException("Deve essere fornito un valore di start minore del valore di end]");
  147.         }
  148.         char oldStart = this.START_CHAR;
  149.         char oldEnd = this.END_CHAR;
  150.         try{
  151.             this.START_CHAR=startChar;
  152.             this.END_CHAR=endChar;
  153.             this.setStartChar(startChar);
  154.             this.setEndChar(endChar);
  155.         }catch(Exception e){
  156.             this.START_CHAR=oldStart;
  157.             this.END_CHAR=oldEnd;
  158.             throw new UtilsException(e.getMessage(),e);
  159.         }
  160.     }
  161.    
  162.     public void setStartEndDigit(char startDigit,char endDigit) throws UtilsException{
  163.         if(startDigit>endDigit){
  164.             throw new UtilsException("Deve essere fornito un valore di start minore del valore di end]");
  165.         }
  166.         char oldStart = this.START_DIGIT;
  167.         char oldEnd = this.END_DIGIT;
  168.         try{
  169.             this.START_DIGIT=startDigit;
  170.             this.END_DIGIT=endDigit;
  171.             this.setStartDigit(startDigit);
  172.             this.setEndDigit(endDigit);
  173.         }catch(Exception e){
  174.             this.START_DIGIT=oldStart;
  175.             this.END_DIGIT=oldEnd;
  176.             throw new UtilsException(e.getMessage(),e);
  177.         }
  178.     }
  179.    
  180.    

  181.     /**
  182.      * Constructor with a default size for the alphanumeric identifier.
  183.      *
  184.      * @param wrap should the factory wrap when it reaches the maximum
  185.      *  long value (or throw an exception)
  186.      */
  187.     public AlphanumericGenerator(boolean wrap) {
  188.         this(wrap, DEFAULT_ALPHANUMERIC_IDENTIFIER_SIZE);
  189.     }

  190.     /**
  191.      * Constructor.
  192.      *
  193.      * @param wrap should the factory wrap when it reaches the maximum
  194.      *  long value (or throw an exception)
  195.      * @param size  the size of the identifier
  196.      */
  197.     public AlphanumericGenerator(boolean wrap, int size) {
  198.         super();
  199.        
  200.         //System.out.println("SIZE ["+size+"]");
  201.        
  202.         this.wrapping = wrap;
  203.         if (size < 1) {
  204.             throw new IllegalArgumentException("The size must be at least one");
  205.         }
  206.         this.count = new char[size];
  207.         for (int i = 0; i < size; i++) {
  208.             this.count[i] = this.START_DIGIT;  // zero
  209.         }
  210.     }

  211.     /**
  212.      * Construct with a counter, that will start at the specified
  213.      * alphanumeric value.</p>
  214.      *
  215.      * @param wrap should the factory wrap when it reaches the maximum
  216.      * value (or throw an exception)
  217.      * @param initialValue the initial value to start at
  218.      */
  219.     public AlphanumericGenerator(boolean wrap, String initialValue) {
  220.         super();
  221.         this.wrapping = wrap;
  222.         this.count = initialValue.toCharArray();
  223.         this.nonAncoraUtilizzato = false;

  224.         //System.out.println("INITIAL ["+initialValue+"]");
  225.        
  226.         char upperStartChar = (this.START_CHAR+"").toUpperCase().charAt(0);
  227.         char lowerStartChar = (this.START_CHAR+"").toLowerCase().charAt(0);
  228.        
  229.         char upperEndChar = (this.END_CHAR+"").toUpperCase().charAt(0);
  230.         char lowerEndChar = (this.END_CHAR+"").toLowerCase().charAt(0);
  231.        
  232.         for (int i = 0; i < this.count.length; i++) {
  233.             char ch = this.count[i];
  234.            
  235.             //System.out.println("CHECK i["+i+"]=["+ch+"]["+(int)ch+"]");
  236.            
  237.             //System.out.println("CHECK DIGIT ["+this.START_DIGIT+"]-["+this.END_DIGIT+"]");
  238.            
  239.             if (ch >= this.START_DIGIT && ch <= this.END_DIGIT) continue;
  240.            
  241.             if(this.lowerChar){
  242.                 //System.out.println("CHECK LOWER ["+lowerStartChar+"]-["+lowerEndChar+"] ["+(int)lowerStartChar+"]-["+(int)lowerEndChar+"]");
  243.                 if (ch >= lowerStartChar && ch <= lowerEndChar) continue;
  244.             }
  245.            
  246.             if(this.upperChar){
  247.                 //System.out.println("CHECK UPPER ["+upperStartChar+"]-["+upperEndChar+"] ["+(int)upperStartChar+"]-["+(int)upperEndChar+"]");
  248.                 if (ch >= upperStartChar && ch <= upperEndChar) continue;
  249.             }

  250.             throw new IllegalArgumentException(
  251.                     "character " + this.count[i] + " is not valid");
  252.         }
  253.     }

  254.     @Override
  255.     public long maxLength() {
  256.         return this.count.length;
  257.     }

  258.     @Override
  259.     public long minLength() {
  260.         return this.count.length;
  261.     }

  262.     /**
  263.      * Getter for property wrap.
  264.      *
  265.      * @return <code>true</code> if this generator is set up to wrap.
  266.      *
  267.      */
  268.     public boolean isWrap() {
  269.         return this.wrapping;
  270.     }

  271.     /**
  272.      * Sets the wrap property.
  273.      *
  274.      * @param wrap value for the wrap property
  275.      *
  276.      */
  277.     public void setWrap(boolean wrap) {
  278.         this.wrapping = wrap;
  279.     }

  280.     /**
  281.      * Returns the (constant) size of the strings generated by this generator.
  282.      *
  283.      * @return the size of generated identifiers
  284.      */
  285.     public int getSize() {
  286.         return this.count.length;
  287.     }

  288.     @Override
  289.     public synchronized String nextStringIdentifier() throws MaxReachedException {
  290.        
  291.         this.nonAncoraUtilizzato = false;
  292.        
  293.         char upperStartChar = (this.START_CHAR+"").toUpperCase().charAt(0);
  294.         char lowerStartChar = (this.START_CHAR+"").toLowerCase().charAt(0);
  295.        
  296.         char upperEndChar = (this.END_CHAR+"").toUpperCase().charAt(0);
  297.         char lowerEndChar = (this.END_CHAR+"").toLowerCase().charAt(0);
  298.        
  299. //      System.out.println("upperStartChar["+upperStartChar+"]");
  300. //      System.out.println("lowerStartChar["+lowerStartChar+"]");
  301. //      System.out.println("upperEndChar["+upperEndChar+"]");
  302. //      System.out.println("lowerEndChar["+lowerEndChar+"]");
  303.        
  304.         for (int i = this.count.length - 1; i >= 0; i--) {
  305.            
  306.             if(this.count[i] == this.END_DIGIT){
  307.                 if(this.lowerChar)
  308.                     this.count[i] = lowerStartChar;
  309.                 else
  310.                     this.count[i] = upperStartChar;
  311.                 i = -1;
  312.             }
  313.             else if(this.lowerChar && this.count[i] == lowerEndChar){
  314.                 if (i == 0 && !this.wrapping) {
  315.                     if(!this.upperChar){
  316.                         throw new MaxReachedException
  317.                         ("The maximum number of identifiers has been reached");
  318.                     }
  319.                 }
  320.                 if(this.upperChar){
  321.                     this.count[i] = upperStartChar;
  322.                     i = -1;
  323.                 }
  324.                 else{
  325.                     this.count[i] = this.START_DIGIT;
  326.                 }
  327.             }
  328.             else if(this.upperChar && this.count[i] == upperEndChar){
  329.                 if (i == 0 && !this.wrapping) {
  330.                     throw new MaxReachedException
  331.                     ("The maximum number of identifiers has been reached");
  332.                 }
  333.                 this.count[i] = this.START_DIGIT;
  334.             }
  335.             else {
  336.                 this.count[i]++;
  337.                 i = -1;
  338.             }
  339.         }
  340.         return new String(this.count);
  341.     }
  342. }