SchemaMapping.java
/*
** Module :SchemaMapping.java
** Abstract :The storage for the schema mapping information.
**
** Copyright (c) 2005-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
** 001 SIY 20050411 ADD @21032 Created initial version
** 002 SIY 20050516 CHG @21203 Updated documentation.
** 003 GBB 20230825 SchemaLoad.REQUIRED reused, replaces local duplicate.
** 004 SP 20250417 Code formatting and javadoc fixes.
*/
/*
** 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 may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.directory;
import java.util.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.goldencode.util.*;
import com.goldencode.p2j.schema.SchemaException;
/**
* This class holds all schema-level mapping information for the LDAP back-end
* and provides methods to read/write data from/to XML. Reading/writing XML is
* performed by implementing <code>XmlProcessor</code> interface. This allows
* to save mapping data in the same file where stored node-level mapping data.
*
* @author SIY
* @version 1.0
*/
class SchemaMapping
implements XmlProcessor
{
/** XML tag for the attribute-to-attribute mapping element */
private static final String ELEM_TAG_ATTRIBUTE_MAPPING = "attribute-mapping";
/** XML tag for the class-to-class mapping element */
private static final String ELEM_TAG_CLASS_MAPPING = "class-mapping";
/** XML tag for the root element */
private static final String ELEM_TAG_ROOT = "schema-storage";
/** List of attribute mappings associated with each LDAP Object Class */
private Map<String, Vector<NamePair>> ldapAttrList;
/** Mapping between LDAP class names and P2J class names */
private Map<String, String> ldapToP2j;
/** List of attribute mappings associated with each P2J Object Class */
private Map<String, Vector<NamePair>> p2jAttrList;
/** Mapping between P2J class names and LDAP class names */
private Map<String, String> p2jToLdap;
/**
* Constructs an instance of SchemaMapping.
*/
SchemaMapping()
{
init();
}
/**
* Retrieve schema mapping from specified XML document after restoring
* directory tree.
*
* @param dom
* Reference to <code>Document</code> instance.
*/
public void postprocess(Document dom)
{
init();
Element elem = dom.getDocumentElement();
Node rootNode = elem.getFirstChild();
while (rootNode != null && !rootNode.getNodeName().equals(ELEM_TAG_ROOT))
{
rootNode = rootNode.getNextSibling();
}
if (rootNode == null)
{
throw new NoSuchElementException("No " + ELEM_TAG_ROOT + " in XML document");
}
Node node = rootNode.getFirstChild();
while (node != null)
{
//System.out.println("XML node " + node.getNodeName());
if (node.getNodeName().equals(ELEM_TAG_CLASS_MAPPING))
{
elem = (Element)node;
String ldapClass = elem.getAttribute("ldap");
String p2jClass = elem.getAttribute("p2j");
if (ldapClass == null || p2jClass == null)
{
continue;
}
mapLdapClassToP2jClass(ldapClass, p2jClass);
NodeList l = node.getChildNodes();
for (int i = 0; i < l.getLength(); i++)
{
if (l.item(i).getNodeName().equals(ELEM_TAG_ATTRIBUTE_MAPPING))
{
elem = (Element)l.item(i);
String ldapName = elem.getAttribute("ldap");
String p2jName = elem.getAttribute("p2j");
if (ldapName == null || p2jName == null)
{
continue;
}
mapAttribute(p2jClass, p2jName, ldapClass, ldapName);
}
}
}
node = node.getNextSibling();
}
}
/**
* Same schema mapping to specified XML document before document will be
* saved into external storage.
*
* @param dom
* Reference to <code>Document</code> instance.
* @param xmlRoot
* Reference to root node.
*/
public void preprocess(Document dom, Node xmlRoot)
{
Element root = dom.createElement(ELEM_TAG_ROOT);
xmlRoot.appendChild(root);
Set<String> nodes = new TreeSet<>(ldapToP2j.keySet());
for (Iterator<String> iter = nodes.iterator(); iter.hasNext();)
{
String ldapClass = iter.next();
if (getP2jClass(ldapClass) == null)
{
throw new NoClassDefFoundError("No P2J class for the " + ldapClass);
}
Element elem = dom.createElement(ELEM_TAG_CLASS_MAPPING);
elem.setAttribute("ldap", ldapClass);
elem.setAttribute("p2j", getP2jClass(ldapClass));
root.appendChild(elem);
Vector<NamePair> mapping = ldapAttrList.get(ldapClass);
if (mapping == null)
{
continue;
}
for (Iterator<NamePair> iterator = mapping.iterator(); iterator.hasNext();)
{
NamePair pair = iterator.next();
Element nodeAttr = dom.createElement(ELEM_TAG_ATTRIBUTE_MAPPING);
nodeAttr.setAttribute("ldap", pair.getLdapAttribute());
nodeAttr.setAttribute("p2j", pair.getP2jAttribute());
elem.appendChild(nodeAttr);
}
}
}
/**
* Check mapping for the presence of required classes.
*
* @throws SchemaException
*/
void checkRequiredClasses()
throws SchemaException
{
for (int i = 0; i < SchemaLoad.REQUIRED.length; i++)
{
if (p2jToLdap.get(SchemaLoad.REQUIRED[i]) == null)
{
throw new SchemaException("Schema definition lacks required "
+ SchemaLoad.REQUIRED[i] + " object class");
}
}
}
/**
* Get LDAP attribute name for specified LDAP class and P2J attribute.
*
* @param p2jClass
* Name of the LDAP Object Class.
* @param p2jAttribute
* Name of the P2J attribute.
*
* @return Name of the LDAP attribute or <code>null</code> if no such
* mapping exists.
*/
String getLdapAttribute(String p2jClass, String p2jAttribute)
{
Vector<NamePair> mapping = p2jAttrList.get(p2jClass);
if (mapping == null)
return null;
for (int i = 0; i < mapping.size(); i++)
{
NamePair element = mapping.get(i);
if (element.getP2jAttribute().equals(p2jAttribute))
{
return element.getLdapAttribute();
}
}
return null;
}
/**
* Get list of LDAP attributes for specified class.
*
* @param ldapClass
* Name of LDAP Object Class.
*
* @return An array of attribute names.
*/
String[] getLdapAttributes(String ldapClass)
{
Vector<NamePair> mapping = ldapAttrList.get(ldapClass);
if (mapping == null)
{
return null;
}
String[] res = new String[mapping.size()];
for (int i = 0; i < res.length; i++)
{
res[i] = (mapping.get(i)).getLdapAttribute();
}
return res;
}
/**
* Return LDAP Object Class name for specified P2J Object Class.
*
* @param p2jClass
* P2J Object Class name.
*
* @return LDAP Object Class name.
*/
String getLdapClass(String p2jClass)
{
return p2jToLdap.get(p2jClass);
}
/**
* Return list of all mappings prepared for storing in LDAP.
*
* @return An array of strings, where each string represents mapping for
* one attribute.
*/
String[] getMapList()
{
ArrayList<String> mapList = new ArrayList<>();
Set<String> keys = new TreeSet<>(ldapToP2j.keySet());
for (Iterator<String> iter = keys.iterator(); iter.hasNext();)
{
String ldapClass = iter.next();
String p2jClass = getP2jClass(ldapClass);
Vector<NamePair> mapping = ldapAttrList.get(ldapClass);
if (mapping == null)
{
continue;
}
for (int i = 0; i < mapping.size(); i++)
{
String ldapAttr = (mapping.get(i)).getLdapAttribute();
String p2jAttr = (mapping.get(i)).getP2jAttribute();
mapList.add(p2jClass + "/" + p2jAttr + "/" + ldapClass + "/" + ldapAttr);
}
}
return mapList.toArray(new String[0]);
}
/**
* Get P2J attribute name for specified P2J class and LDAP attribute.
*
* @param p2jClass
* Name of the P2J Object Class.
* @param ldapAttribute
* Name of the LDAP attribute.
*
* @return Name of the P2J attribute or <code>null</code> if no such
* mapping exists.
*/
String getP2jAttribute(String p2jClass, String ldapAttribute)
{
Vector<NamePair> mapping = p2jAttrList.get(p2jClass);
if (mapping == null)
{
return null;
}
for (int i = 0; i < mapping.size(); i++)
{
NamePair element = mapping.get(i);
if (element.getLdapAttribute().equals(ldapAttribute))
{
return element.getP2jAttribute();
}
}
return null;
}
/**
* Get list of P2J attributes for specified class.
*
* @param p2jClass
* Name of LDAP Object Class.
*
* @return An array of attribute names.
*/
String[] getP2jAttributes(String p2jClass)
{
Vector<NamePair> mapping = p2jAttrList.get(p2jClass);
if (mapping == null)
{
return null;
}
String[] res = new String[mapping.size()];
for (int i = 0; i < res.length; i++)
{
res[i] = (mapping.get(i)).getP2jAttribute();
}
return res;
}
/**
* Return P2J Object Class name for specified LDAP Object Class.
*
* @param ldapClass
* Name of the LDAP Object Class.
*
* @return P2J Object Class name.
*/
String getP2jClass(String ldapClass)
{
return ldapToP2j.get(ldapClass);
}
/**
* Create mapping between specified attributes.
*
* @param p2jClass
* Name of the P2J Object Class.
* @param p2jAttribute
* Name of the attribute of the P2J Object Class.
* @param ldapClass
* Name of the LDAP Object Class.
* @param ldapAtribute
* Name of the attribute of the LDAP Object Class.
*/
void mapAttribute(String p2jClass, String p2jAttribute, String ldapClass,
String ldapAtribute)
{
Vector<NamePair> mapping = ldapAttrList.get(ldapClass);
if (mapping == null)
{
mapping = new Vector<>();
}
NamePair pair = new NamePair(ldapAtribute, p2jAttribute);
//Check if such a mapping already exists
boolean present = false;
for (int i = 0; i < mapping.size(); i++)
{
NamePair element = mapping.get(i);
if (element.getP2jAttribute().equals(p2jAttribute))
{
present = true;
mapping.set(i, pair);
}
}
if (!present)
{
mapping.add(pair);
}
ldapAttrList.put(ldapClass, mapping);
p2jAttrList.put(p2jClass, mapping);
}
/**
* Create mapping between LDAP and P2J Object Classes.
*
* @param ldapClass
* Name of the LDAP Object Class.
* @param p2jClass
* Name of the P2J Object Class.
*/
void mapLdapClassToP2jClass(String ldapClass, String p2jClass)
{
p2jToLdap.put(p2jClass, ldapClass);
ldapToP2j.put(ldapClass, p2jClass);
}
/**
* Create mapping between LDAP and P2J Object Classes.
*
* @param p2jClass
* Name of the P2J Object Class.
* @param ldapClass
* Name of the LDAP Object Class.
*/
void mapP2jClassToLdap(String p2jClass, String ldapClass)
{
p2jToLdap.put(p2jClass, ldapClass);
ldapToP2j.put(ldapClass, p2jClass);
}
/**
* Create storage
*/
private void init()
{
ldapToP2j = new CaseInsensitiveHashMap<>();
p2jToLdap = new CaseInsensitiveHashMap<>();
ldapAttrList = new CaseInsensitiveHashMap<>();
p2jAttrList = new CaseInsensitiveHashMap<>();
}
/**
* This class is used to store attribute mapping.
*/
static class NamePair
{
/** LDAP attribute name */
private String ldapAttribute;
/** P2J attribute name */
private String p2jAttribute;
/**
* Construct an instance of NamePaitr for specified pair of attribute
* names.
*
* @param ldap
* LDAP attribute name.
* @param p2j
* P2J attribute name.
*/
NamePair(String ldap, String p2j)
{
ldapAttribute = ldap;
p2jAttribute = p2j;
}
/**
* Get name of the LDAP attribute.
*
* @return LDAP attribute name.
*/
String getLdapAttribute()
{
return ldapAttribute;
}
/**
* Get name of the P2J attribute.
*
* @return P2J attribute name.
*/
String getP2jAttribute()
{
return p2jAttribute;
}
}
}