/*
* (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Nuxeo - initial API and implementation
* Florent Guillaume
*/
package org.nuxeo.ecm.directory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.common.xmap.annotation.XNode;
import org.nuxeo.common.xmap.annotation.XNodeList;
import org.nuxeo.common.xmap.annotation.XObject;
import org.nuxeo.ecm.directory.api.DirectoryDeleteConstraint;
/**
* Basic directory descriptor, containing the basic fields used by all directories.
*
* @since 8.2
*/
@XObject(value = "directory")
public class BaseDirectoryDescriptor implements Cloneable {
private static final Log log = LogFactory.getLog(BaseDirectoryDescriptor.class);
/**
* How directory semi-"fulltext" searches are matched with a query string.
* <p>
* Used for SQL and LDAP directories.
*
* @since 8.2
*/
public enum SubstringMatchType {
/** Matches initial substring. */
subinitial,
/** Matches final substring. */
subfinal,
/** Matches any substring. */
subany;
}
public static final boolean AUTO_INCREMENT_ID_FIELD_DEFAULT = false;
public static final int CACHE_TIMEOUT_DEFAULT = 0;
public static final int CACHE_MAX_SIZE_DEFAULT = 0;
public static final boolean READ_ONLY_DEFAULT = false;
public static final SubstringMatchType SUBSTRING_MATCH_TYPE_DEFAULT = SubstringMatchType.subinitial;
public static final char DEFAULT_DATA_FILE_CHARACTER_SEPARATOR = ',';
/**
* Doesn't create or modify the table in any way.
*/
public static final String CREATE_TABLE_POLICY_NEVER = "never";
/**
* Always recreates the table from scratch and loads the CSV data.
*/
public static final String CREATE_TABLE_POLICY_ALWAYS = "always";
/**
* If the table doesn't exist then creates it and loads the CSV data. If the table already exists, only adds missing
* columns (with null values).
*/
public static final String CREATE_TABLE_POLICY_ON_MISSING_COLUMNS = "on_missing_columns";
public static final String CREATE_TABLE_POLICY_DEFAULT = CREATE_TABLE_POLICY_NEVER;
public static final List<String> CREATE_TABLE_POLICIES = Arrays.asList(CREATE_TABLE_POLICY_NEVER,
CREATE_TABLE_POLICY_ALWAYS, CREATE_TABLE_POLICY_ON_MISSING_COLUMNS);
@XNode("@name")
public String name;
@XNode("@remove")
public boolean remove;
@XNode("@template")
public boolean template;
@XNode("@extends")
public String extendz;
@XNode("parentDirectory")
public String parentDirectory;
@XNode("schema")
public String schemaName;
@XNode("idField")
public String idField;
@XNode("autoincrementIdField")
public Boolean autoincrementIdField;
public boolean isAutoincrementIdField() {
return autoincrementIdField == null ? AUTO_INCREMENT_ID_FIELD_DEFAULT : autoincrementIdField.booleanValue();
}
public void setAutoincrementIdField(boolean autoincrementIdField) {
this.autoincrementIdField = Boolean.valueOf(autoincrementIdField);
}
@XNode("table")
public String tableName;
@XNode("readOnly")
public Boolean readOnly;
@XNode("passwordField")
public String passwordField;
@XNode("passwordHashAlgorithm")
public String passwordHashAlgorithm;
@XNodeList(value = "permissions/permission", type = PermissionDescriptor[].class, componentType = PermissionDescriptor.class)
public PermissionDescriptor[] permissions;
@XNode("cacheTimeout")
public Integer cacheTimeout;
@XNode("cacheMaxSize")
public Integer cacheMaxSize;
@XNode("cacheEntryName")
public String cacheEntryName;
@XNode("cacheEntryWithoutReferencesName")
public String cacheEntryWithoutReferencesName;
@XNode("negativeCaching")
public Boolean negativeCaching;
@XNode("substringMatchType")
public String substringMatchType;
/**
* @since 8.4
*/
@XNodeList(value = "types/type", type = String[].class, componentType = String.class)
public String[] types;
/**
* @since 8.4
*/
@XNodeList(value = "deleteConstraint", type = ArrayList.class, componentType = DirectoryDeleteConstraintDescriptor.class)
List<DirectoryDeleteConstraintDescriptor> deleteConstraints;
@XNode("dataFile")
public String dataFileName;
public String getDataFileName() {
return dataFileName;
}
@XNode(value = "dataFileCharacterSeparator", trim = false)
public String dataFileCharacterSeparator = ",";
public char getDataFileCharacterSeparator() {
char sep;
if (StringUtils.isEmpty(dataFileCharacterSeparator)) {
sep = DEFAULT_DATA_FILE_CHARACTER_SEPARATOR;
} else {
sep = dataFileCharacterSeparator.charAt(0);
if (dataFileCharacterSeparator.length() > 1) {
log.warn("More than one character found for character separator, will use the first one \"" + sep
+ "\"");
}
}
return sep;
}
@XNode("createTablePolicy")
public String createTablePolicy;
public String getCreateTablePolicy() {
if (StringUtils.isBlank(createTablePolicy)) {
return CREATE_TABLE_POLICY_DEFAULT;
}
String ctp = createTablePolicy.toLowerCase();
if (!CREATE_TABLE_POLICIES.contains(ctp)) {
throw new DirectoryException("Invalid createTablePolicy: " + createTablePolicy + ", it should be one of: "
+ CREATE_TABLE_POLICIES);
}
return ctp;
}
public boolean isReadOnly() {
return readOnly == null ? READ_ONLY_DEFAULT : readOnly.booleanValue();
}
public void setReadOnly(boolean readOnly) {
this.readOnly = Boolean.valueOf(readOnly);
}
public int getCacheTimeout() {
return cacheTimeout == null ? CACHE_TIMEOUT_DEFAULT : cacheTimeout.intValue();
}
public int getCacheMaxSize() {
return cacheMaxSize == null ? CACHE_MAX_SIZE_DEFAULT : cacheMaxSize.intValue();
}
public SubstringMatchType getSubstringMatchType() {
if (StringUtils.isBlank(substringMatchType)) {
return SUBSTRING_MATCH_TYPE_DEFAULT;
}
try {
return SubstringMatchType.valueOf(substringMatchType);
} catch (IllegalArgumentException e) {
log.error("Unknown value for <substringMatchType>: " + substringMatchType);
return SUBSTRING_MATCH_TYPE_DEFAULT;
}
}
/**
* Sub-classes MUST OVERRIDE and use a more specific return type.
* <p>
* Usually it's bad to use clone(), and a copy-constructor is preferred, but here we want the copy method to be
* inheritable so clone() is appropriate.
* <p>
* {@inheritDoc}
*/
@Override
public BaseDirectoryDescriptor clone() {
BaseDirectoryDescriptor clone;
try {
clone = (BaseDirectoryDescriptor) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
// basic fields are already copied by super.clone()
if (permissions != null) {
clone.permissions = new PermissionDescriptor[permissions.length];
for (int i = 0; i < permissions.length; i++) {
clone.permissions[i] = permissions[i].clone();
}
}
return clone;
}
public void merge(BaseDirectoryDescriptor other) {
template = template || other.template;
if (other.parentDirectory != null) {
parentDirectory = other.parentDirectory;
}
if (other.schemaName != null) {
schemaName = other.schemaName;
}
if (other.idField != null) {
idField = other.idField;
}
if (other.autoincrementIdField != null) {
autoincrementIdField = other.autoincrementIdField;
}
if (other.tableName != null) {
tableName = other.tableName;
}
if (other.readOnly != null) {
readOnly = other.readOnly;
}
if (other.passwordField != null) {
passwordField = other.passwordField;
}
if (other.passwordHashAlgorithm != null) {
passwordHashAlgorithm = other.passwordHashAlgorithm;
}
if (other.permissions != null && other.permissions.length != 0) {
permissions = other.permissions;
}
if (other.cacheTimeout != null) {
cacheTimeout = other.cacheTimeout;
}
if (other.cacheMaxSize != null) {
cacheMaxSize = other.cacheMaxSize;
}
if (other.cacheEntryName != null) {
cacheEntryName = other.cacheEntryName;
}
if (other.cacheEntryWithoutReferencesName != null) {
cacheEntryWithoutReferencesName = other.cacheEntryWithoutReferencesName;
}
if (other.negativeCaching != null) {
negativeCaching = other.negativeCaching;
}
if (other.substringMatchType != null) {
substringMatchType = other.substringMatchType;
}
if (other.types != null) {
types = other.types;
}
if (other.deleteConstraints != null) {
deleteConstraints = other.deleteConstraints;
}
if (other.dataFileName != null) {
dataFileName = other.dataFileName;
}
if (other.dataFileCharacterSeparator != null) {
dataFileCharacterSeparator = other.dataFileCharacterSeparator;
}
if (other.createTablePolicy != null) {
createTablePolicy = other.createTablePolicy;
}
}
/**
* Creates a new {@link Directory} instance from this {@link DirectoryDescriptor).
*/
public Directory newDirectory() {
throw new UnsupportedOperationException("Cannot be instantiated as Directory: " + getClass().getName());
}
/**
* @since 8.4
*/
public List<DirectoryDeleteConstraint> getDeleteConstraints() throws DirectoryException {
List<DirectoryDeleteConstraint> res = new ArrayList<DirectoryDeleteConstraint>();
if (deleteConstraints != null) {
for (DirectoryDeleteConstraintDescriptor deleteConstraintDescriptor : deleteConstraints) {
res.add(deleteConstraintDescriptor.getDeleteConstraint());
}
}
return res;
}
}