code: /*******************************************************************************
   * Copyright (c) 2013 Attila Kovacs <attila[AT]sigmyne.com>.
   * All rights reserved. 
   * 
   * This file is part of crush.
   * 
   *     crush is free software: you can redistribute it and/or modify
   *     it under the terms of the GNU General Public License as published by
   *     the Free Software Foundation, either version 3 of the License, or
   *     (at your option) any later version.
   * 
   *     crush is distributed in the hope that it will be useful,
   *     but WITHOUT ANY WARRANTY; without even the implied warranty of
   *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *     GNU General Public License for more details.
   * 
   *     You should have received a copy of the GNU General Public License
   *     along with crush.  If not, see <http://www.gnu.org/licenses/>.
   * 
   * Contributors:
   *     Attila Kovacs <attila[AT]sigmyne.com> - initial API and implementation
   ******************************************************************************/
  package crush.telescope.iram;
  
  
  import java.util.StringTokenizer;
  
  import jnum.Util;
  import jnum.math.Range;
  
  
  // 2012-04-12.138
  // 01234567890123
  
  public class IRAMScanID implements Comparable<IRAMScanID> {
  	int year, month, day, scanNumber;
  	
  	public IRAMScanID() {}
  	
  	public IRAMScanID(String text) throws NumberFormatException { parse(text); }
  	
  	@Override
  	public int hashCode() {
  		return year ^ ~month ^ (day << 8) ^ (scanNumber << 12);  
  	}
  	
  	@Override
  	public boolean equals(Object o) {
  		if(this == o) return true;
  		if(!(o instanceof IRAMScanID)) return false;
  		
  		IRAMScanID other = (IRAMScanID) o;
  		if(year != other.year) return false;
  		if(month != other.month) return false;
  		if(day != other.day) return false;
  		if(scanNumber != other.scanNumber) return false;
  		return true;		
  	}
  	
  	public void parse(String id) throws NumberFormatException {
  		if(!(id.charAt(4) == '-' && id.charAt(7) == '-' && id.charAt(10) == '.')) 
  			throw new NumberFormatException(" Not a valid IRAM scan ID: '" + id + "'.");
  		
  		year = Integer.parseInt(id.substring(0, 4));
  		month = Integer.parseInt(id.substring(5, 7));
  		day = Integer.parseInt(id.substring(8, 10));
  		scanNumber = Integer.parseInt(id.substring(11));
  	}
  	
  	@Override
  	public String toString() {
  		return Util.d4.format(year) + "-" + Util.d2.format(month) + "-" + Util.d2.format(day) 
  				+ "." + scanNumber;
  	}
  	
  	// return a double that is equal to YYYYMMDD.nnnn
  	public double asDouble() {
  		return 1e4 * year + 100.0 * month + day + 1e-4 * scanNumber;
  	}
  
  	@Override
  	public int compareTo(IRAMScanID other) {
  		if(year != other.year) return year < other.year ? -1 : 1;
  		if(month != other.month) return month < other.month ? -1 : 1;
  		if(day != other.day) return day < other.day ? -1 : 1;
  		if(scanNumber != other.scanNumber) return scanNumber < other.scanNumber ? -1 : 1;
  		return 0;
  	}
  	
  	public static Range rangeFor(String rangeSpec) {
  		StringTokenizer tokens = new StringTokenizer(rangeSpec, ":");
  		Range scanRange = new Range();
  
  		String spec = tokens.nextToken();
  		scanRange.setMin(spec.equals("*") ? Double.NEGATIVE_INFINITY : new IRAMScanID(spec).asDouble());
  		scanRange.setMax(Double.POSITIVE_INFINITY);
  		if(tokens.hasMoreTokens()) {
  			spec = tokens.nextToken();
  			scanRange.setMax(spec.equals("*") ? Double.POSITIVE_INFINITY : new IRAMScanID(spec).asDouble());
  		}
  
  		return scanRange;
  	}
  	
  }
  