/*
* Copyright (c) 2006-2009 by Dirk Riehle, http://dirkriehle.com
*
* This file is part of the Wahlzeit photo rating application.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
package org.wahlzeit.model;
import java.io.Serializable;
import java.util.Random;
/**
* A photo id identifies a photo with a unique number.
* The number has an equivalent string for web access.
* This class also hands out the ids.
*/
public class PhotoId implements Serializable {
/**
* 0 is never returned from nextValue; first value is 1
*/
protected static int currentId = 0;
/**
*
*/
public static final int BUFFER_SIZE_INCREMENT = 64;
/**
*
*/
public static final PhotoId NULL_ID = new PhotoId(0);
/**
*
*/
protected static PhotoId[] ids = new PhotoId[BUFFER_SIZE_INCREMENT];
/**
* What a hack :-)
*/
public static final int ID_START = getFromString("x1abz") + 1;
/**
*
*/
protected static Random randomNumber = new Random(System.currentTimeMillis());
private PhotoId() {
// do nothing, necessary for Objectify to load PhotoIds
}
/**
*
*/
public static int getCurrentIdAsInt() {
return currentId;
}
/**
*
*/
public static synchronized void setCurrentIdFromInt(int id) {
currentId = id;
ids = new PhotoId[currentId + BUFFER_SIZE_INCREMENT];
ids[0] = NULL_ID;
}
/**
*
*/
public static synchronized int getNextIdAsInt() {
currentId += 1;
if (currentId >= ids.length) {
PhotoId[] nids = new PhotoId[currentId + BUFFER_SIZE_INCREMENT];
System.arraycopy(ids, 0, nids, 0, currentId);
ids = nids;
}
return currentId;
}
/**
*
*/
public static PhotoId getIdFromInt(int id) {
if ((id < 0) || (id > currentId)) {
return NULL_ID;
}
// @FIXME http://en.wikipedia.org/wiki/Double-checked_locking
PhotoId result = ids[id];
if (result == null) {
synchronized (ids) {
result = ids[id];
if (result == null) {
result = new PhotoId(id);
ids[id] = result;
}
}
}
return result;
}
/**
*
*/
public static PhotoId getIdFromString(String id) {
return getIdFromInt(getFromString(id));
}
/**
*
*/
public static PhotoId getNextId() {
return getIdFromInt(getNextIdAsInt());
}
/**
*
*/
public static PhotoId getRandomId() {
int max = getCurrentIdAsInt() - 1;
int id = randomNumber.nextInt();
id = (id == Integer.MIN_VALUE) ? id++ : id;
id = (Math.abs(id) % max) + 1;
return getIdFromInt(id);
}
/**
*
*/
protected int value = 0;
protected String stringValue = null;
/**
*
*/
protected PhotoId(int myValue) {
value = myValue;
stringValue = getFromInt(myValue);
}
/**
*
*/
public boolean equals(Object o) {
// @FIXME
if (!(o instanceof PhotoId)) {
return false;
}
PhotoId pid = (PhotoId) o;
return isEqual(pid);
}
/**
*
*/
public boolean isEqual(PhotoId other) {
return other.value == value;
}
/**
* @methodtype get
*/
public int hashCode() {
return value;
}
/**
*
*/
public boolean isNullId() {
return this == NULL_ID;
}
/**
*
*/
public int asInt() {
return value;
}
/**
*
*/
public String asString() {
return stringValue;
}
/**
*
*/
public static String getFromInt(int id) {
StringBuffer result = new StringBuffer(10);
id += ID_START;
for (; id > 0; id = id / 36) {
char letterOrDigit;
int modulus = id % 36;
if (modulus < 10) {
letterOrDigit = (char) ((int) '0' + modulus);
} else {
letterOrDigit = (char) ((int) 'a' - 10 + modulus);
}
result.insert(0, letterOrDigit);
}
return "x" + result.toString();
}
/**
*
*/
public static int getFromString(String value) {
int result = 0;
for (int i = 1; i < value.length(); i++) {
int temp = 0;
char letterOrDigit = value.charAt(i);
if (letterOrDigit < 'a') {
temp = (int) letterOrDigit - '0';
} else {
temp = 10 + (int) letterOrDigit - 'a';
}
result = result * 36 + temp;
}
result -= ID_START;
if (result < 0) {
result = 0;
}
return result;
}
}