/*******************************************************************************
* Copyright (c) 2007-2011, G. Weirich and Elexis
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* G. Weirich - initial implementation
*
*******************************************************************************/
package ch.elexis.data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import static ch.elexis.core.constants.XidConstants.*;
import ch.elexis.core.constants.XidConstants;
import ch.elexis.core.data.activator.CoreHub;
import ch.elexis.core.model.IPersistentObject;
import ch.elexis.core.model.IXid;
import ch.rgw.tools.Log;
import ch.rgw.tools.VersionInfo;
/**
* A XID is an external identifier, that is an ID from en external identifyer system. Examples are
* entities such as Social Security Number, Passport Number, OID, and others. XID's are not limites
* to Persons but can be attributed to all kinds of entities. They are usable like OID's but are
* more general and can integrate other systems. A XID consists of a domain that denotes the
* identifying system and an ID within this domain. The Domain name is globally unique, while the ID
* can be globally unique, but might also be unique within its domain only. To differentiate between
* such "qualities" there is a flag that indicates, that a XID is in fact a GUID. There is also a
* flag to indicate the range within a xid is valid (e.g. a social security number ist only an
* identifier within the country where it was created) The Flag ASSIGNMENT_LOCAL means that this XID
* is used only between different instances of this program, ASSIGNMENT_REGIONAL means a XID used
* within a country while ASSIGNMENT_GLOBAL a globally used identifier is (e.g. an EAN or an OID)
*
* To simplify working with XIDs for the user, a XID Domain can also have a short "nickname" that is
* valid and unique within the running instance of the program only and that is mapped to a full
* xid-domain. The Domain "www.xid.ch/id/ean" can be called "EAN". The two namings ar exchangeable
* within the defining instance, but a XID that leaves this instance of the program must always be
* named with its full name.
*
* @author Gerry
*
*/
public class Xid extends PersistentObject implements IXid {
public static final String FLD_OBJECT = "object";
public static final String FLD_ID_IN_DOMAIN = "domain_id";
public static final String FLD_DOMAIN = "domain";
public static final String FLD_QUALITY = "quality";
public static final String FLD_TYPE = "type";
private static final String VERSION = "1.0.0";
private static final String TABLENAME = "XID";
private static Log log = Log.get("XID");
/**
* Quality value for an ID that is valid only in the context of the issuing program
*/
public static final int ASSIGNMENT_LOCAL = XidConstants.ASSIGNMENT_LOCAL;
/**
* Quality value for an ID that is valid within a geographic or politic context (e.g. a
* nationally assigned ID)
*/
public static final int ASSIGNMENT_REGIONAL = XidConstants.ASSIGNMENT_REGIONAL;
/**
* Quality value for an ID that can be used as global identifier
*/
public static final int ASSIGNMENT_GLOBAL = XidConstants.ASSIGNMENT_GLOBAL;
/**
* Marker that the ID is a GUID (that is, guaranteed to exist only once through time and space)
*/
public static final int QUALITY_GUID = XidConstants.QUALITY_GUID;
private static HashMap<String, XIDDomain> domains;
private static HashMap<String, String> domainMap;
static {
addMapping(TABLENAME, FLD_TYPE, FLD_OBJECT, FLD_DOMAIN, FLD_ID_IN_DOMAIN, FLD_QUALITY);
domains = new HashMap<String, XIDDomain>();
domainMap = new HashMap<String, String>();
String storedDomains = CoreHub.globalCfg.get("LocalXIDDomains", null);
if (storedDomains == null) {
domains.put(ELEXIS, new XIDDomain(ELEXIS, "UUID", ELEXIS_QUALITY | QUALITY_GUID,
PersistentObject.class.getCanonicalName()));
domains.put(CH_AHV, new XIDDomain(CH_AHV, "AHV", CH_AHV_QUALITY, Person.class.getCanonicalName()));
domains.put(DOMAIN_OID, new XIDDomain(DOMAIN_OID, "OID", ASSIGNMENT_GLOBAL | QUALITY_GUID,
PersistentObject.class.getCanonicalName()));
domains.put(DOMAIN_EAN,
new XIDDomain(DOMAIN_EAN, "EAN", ASSIGNMENT_REGIONAL, Kontakt.class.getCanonicalName()));
storeDomains();
} else {
for (String dom : storedDomains.split(";")) {
String[] spl = dom.split("#");
if (spl.length < 2) {
log.log("Fehler in XID-Domain " + dom, Log.ERRORS);
}
String simpleName = "";
if (spl.length >= 3) {
simpleName = spl[2];
}
String displayOptions = "Kontakt";
if (spl.length >= 4) {
displayOptions = spl[3];
}
domains.put(spl[0], new XIDDomain(spl[0], simpleName, Integer.parseInt(spl[1]),
displayOptions));
domainMap.put(simpleName, spl[0]);
}
}
VersionInfo vv = new ch.rgw.tools.VersionInfo(CoreHub.Version);
if (vv.isOlder("1.3.2")) {
XIDDomain xd = domains.get(DOMAIN_EAN);
xd.addDisplayOption(Person.class);
xd.addDisplayOption(Organisation.class);
xd = domains.get(DOMAIN_AHV);
xd.addDisplayOption(Person.class);
}
}
/**
* create a new XID. Does nothing if identical XID already exists.
*
* @param o
* the object to identify with the new XID
* @param domain
* the domain from wich the identifier is (e.g. DOMAIN_COVERCARD). Must be a
* registered domain
* @param domain_id
* the id from that domain that identifies the object
* @param quality
* the quality of this identifier
* @throws XIDException
* if a XID with same domain and domain_id but different object or quality already
* exists. if the domain was not rgeistered
*/
public Xid(final PersistentObject o, final String domain, final String domain_id)
throws XIDException{
XIDDomain dom = domains.get(domain);
if (dom == null) {
throw new XIDException("Domain not registered: " + domain);
}
Integer val = dom.quality;
if (val == null) {
throw new XIDException("XID Domain " + domain + " is not registered");
}
if (val > 9) {
val = (val & 7) + 4;
}
Xid xid = findXID(domain, domain_id);
if (xid != null) {
if (xid.get(FLD_OBJECT).equals(o.getId())) {
return;
}
throw new XIDException("XID " + domain + ":" + domain_id + " is not unique");
}
xid = findXID(o, domain);
if (xid != null) {
throw new XIDException("XID " + domain + ": " + domain_id + " was already assigned");
}
create(null);
set(new String[] {
FLD_TYPE, FLD_OBJECT, FLD_DOMAIN, FLD_ID_IN_DOMAIN, FLD_QUALITY
}, new String[] {
o.getClass().getName(), o.getId(), domain, domain_id, Integer.toString(val)
});
}
/**
* Get the quality of this xid
*
* @return the quality
*/
public int getQuality(){
return checkZero(get(FLD_QUALITY));
}
/**
* Tell whether this XID is a GUID
*
* @return true if so.
*/
public boolean isGUID(){
return (getQuality() & QUALITY_GUID) != 0;
}
/**
* get the Domain this Xid is from
*
* @return
*/
public String getDomain(){
return get(FLD_DOMAIN);
}
/**
* get the id of this Xid in its domain
*
* @return
*/
public String getDomainId(){
return get(FLD_ID_IN_DOMAIN);
}
/**
* Get the object that is identified with this XID
*
* @return the object or null if it could not be restored.
*/
public IPersistentObject getObject(){
PersistentObject po =
CoreHub.poFactory.createFromString(get(FLD_TYPE) + "::" + get(FLD_OBJECT));
return po;
}
@Override
public String getLabel(){
IPersistentObject po = getObject();
String text = "unknown object";
if (po != null) {
text = po.getLabel();
}
StringBuilder ret = new StringBuilder();
ret.append(text).append(": ").append(get(FLD_DOMAIN)).append("->")
.append(get(FLD_ID_IN_DOMAIN));
return ret.toString();
}
public static Xid load(final String id){
return new Xid(id);
}
/**
* Find a XID from a domain and a domain_id
*
* @param domain
* the domain to search an id from, Can be full name or local short name of the
* domain
* @param id
* the id out of domain to retrieve
* @return the xid holding that id from that domain or null if no such xid was found
*/
public static Xid findXID(String domain, final String id){
String dom = domainMap.get(domain);
if (dom != null) {
domain = dom;
}
Query<Xid> qbe = new Query<Xid>(Xid.class);
qbe.add(FLD_DOMAIN, Query.EQUALS, domain);
qbe.add(FLD_ID_IN_DOMAIN, Query.EQUALS, id);
List<Xid> ret = qbe.execute();
if (ret.size() == 1) {
Xid result = ret.get(0);
IPersistentObject po = result.getObject();
if (po != null && po.exists()) {
return result;
} else {
result.delete();
}
}
return null;
}
/**
* Find a PersistentObject from a domain and a domain_id
*
* @param domain
* the domain to search an id from (e.g. www.ahv.ch)
* @param id
* the id out of domain to retrieve
* @return the PersistentObject identified by that id from that domain or null if no such Object
* was found
*/
public static IPersistentObject findObject(final String domain, final String id){
Xid xid = findXID(domain, id);
if (xid != null) {
return xid.getObject();
}
return null;
}
/**
* Find the Xid of a given domain for the given Object
*
* @param o
* the object whose Xid should be find
* @param domain
* the domain the Xid should be from
* @return the Xid or null if no xid for the given domain was found on the given object
*/
public static Xid findXID(final PersistentObject o, String domain){
String dom = domainMap.get(domain);
if (dom != null) {
domain = dom;
}
Query<Xid> qbe = new Query<Xid>(Xid.class);
qbe.add(FLD_DOMAIN, Query.EQUALS, domain);
qbe.add(FLD_OBJECT, Query.EQUALS, o.getId());
List<Xid> ret = qbe.execute();
if (ret.size() == 1) {
return ret.get(0);
}
return null;
}
/**
* Register a new domain for use with our XID System locally (this will not affect the central
* XID registry at www.xid.ch)
*
* @param domain
* the domain to register
* @param quality
* the quality an ID of that domain will have
* @return true on success, false if that domain could not be registered
*/
public static boolean localRegisterXIDDomain(final String domain, String simpleName,
final int quality){
if (domains.containsKey(domain)) {
log.log("XID Domain " + domain + " bereits registriert", Log.ERRORS);
} else {
if (domain.matches(".*[;#].*")) {
log.log("XID Domain " + domain + " ungültig", Log.ERRORS);
} else {
domains.put(domain, new XIDDomain(domain, simpleName == null ? "" : simpleName,
quality, "Kontakt"));
if (simpleName != null) {
domainMap.put(simpleName, domain);
}
storeDomains();
return true;
}
}
return false;
}
/**
* Register a local xid domain if it does not exist. Does nothing if a domain with the given
* domain name exists already
*
* @param domain
* name of the domain
* @param simpleName
* short name for the domain
* @param quality
* the wuality of an ID of that domain will have
* @return true on success
*/
public static boolean localRegisterXIDDomainIfNotExists(final String domain, String simpleName,
final int quality){
if (domains.get(domain) != null) {
return true;
}
return localRegisterXIDDomain(domain, simpleName, quality);
}
/**
* Get the ID quality of an Object of a given domain
*
* @param xidDomain
* the domain to query
* @return obne of the Quality-ID constants or null if no such domain ist registered
*/
public static Integer getXIDDomainQuality(final String xidDomain){
XIDDomain xd = domains.get(xidDomain);
if (xd == null) {
return null;
}
return xd.getQuality();
}
public static String getSimpleNameForXIDDomain(final String domain){
XIDDomain xd = domains.get(domain);
if (xd == null) {
return domain;
}
return xd.simple_name;
}
public static XIDDomain getDomain(String name){
String dom = domainMap.get(name);
if (dom != null) {
name = dom;
}
return domains.get(name);
}
protected Xid(final String id){
super(id);
}
protected Xid(){}
@Override
protected String getTableName(){
return TABLENAME;
}
@SuppressWarnings("serial")
public static class XIDException extends Exception {
public XIDException(final String reason){
super(reason);
}
}
private static void storeDomains(){
StringBuilder sb = new StringBuilder();
for (String k : domains.keySet()) {
XIDDomain xd = domains.get(k);
sb.append(k).append("#").append(xd.getQuality()).append("#").append(xd.getSimpleName())
.append("#").append(xd.getDisplayOptions()).append(";");
}
CoreHub.globalCfg.set("LocalXIDDomains", sb.toString());
}
/**
* return a list of all known domains
*
* @return
*/
public static Set<String> getXIDDomains(){
return domains.keySet();
}
@Override
public boolean undelete(){
new DBLog(this, DBLog.TYP.UNDELETE);
return (set("deleted", "0"));
};
public static class XIDDomain {
String domain_name;
String simple_name;
int quality;
ArrayList<Class<? extends PersistentObject>> displayOptions =
new ArrayList<Class<? extends PersistentObject>>();
@SuppressWarnings("unchecked")
public XIDDomain(String dname, String simplename, int quality, String options){
domain_name = dname;
simple_name = simplename;
this.quality = quality;
for (String op : options.split(",")) {
try {
Class clazz = Class.forName(op);
displayOptions.add(clazz);
} catch (Exception ex) {}
}
}
public String getSimpleName(){
return simple_name;
}
public void setSimpleName(String simple_name){
this.simple_name = simple_name;
storeDomains();
}
public String getDomainName(){
return domain_name;
}
public int getQuality(){
return quality;
}
public void addDisplayOption(Class<? extends PersistentObject> clazz){
if (!displayOptions.contains(clazz)) {
displayOptions.add(clazz);
storeDomains();
}
}
public void removeDisplayOption(Class<? extends PersistentObject> clazz){
displayOptions.remove(clazz);
storeDomains();
}
public boolean isDisplayedFor(Class<? extends PersistentObject> clazz){
return displayOptions.contains(clazz);
}
String getDisplayOptions(){
StringBuilder r = new StringBuilder();
for (Class<? extends PersistentObject> clazz : displayOptions) {
r.append(clazz.getName()).append(",");
}
return r.toString();
}
}
}