001/*
002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *     Florent Guillaume
019 */
020package org.nuxeo.ecm.directory;
021
022import org.apache.commons.lang.StringUtils;
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025import org.nuxeo.common.xmap.annotation.XNode;
026import org.nuxeo.common.xmap.annotation.XNodeList;
027import org.nuxeo.common.xmap.annotation.XObject;
028
029/**
030 * Basic directory descriptor, containing the basic fields used by all directories.
031 *
032 * @since 8.2
033 */
034@XObject(value = "directory")
035public class BaseDirectoryDescriptor implements Cloneable {
036
037    private static final Log log = LogFactory.getLog(BaseDirectoryDescriptor.class);
038
039    /**
040     * How directory semi-"fulltext" searches are matched with a query string.
041     * <p>
042     * Used for SQL and LDAP directories.
043     *
044     * @since 8.2
045     */
046    public enum SubstringMatchType {
047        /** Matches initial substring. */
048        subinitial,
049        /** Matches final substring. */
050        subfinal,
051        /** Matches any substring. */
052        subany;
053    }
054
055    public static final int CACHE_TIMEOUT_DEFAULT = 0;
056
057    public static final int CACHE_MAX_SIZE_DEFAULT = 0;
058
059    public static final boolean READ_ONLY_DEFAULT = false;
060
061    public static final SubstringMatchType SUBSTRING_MATCH_TYPE_DEFAULT = SubstringMatchType.subinitial;
062
063    @XNode("@name")
064    public String name;
065
066    @XNode("@remove")
067    public boolean remove;
068
069    @XNode("@template")
070    public boolean template;
071
072    @XNode("@extends")
073    public String extendz;
074
075    @XNode("parentDirectory")
076    public String parentDirectory;
077
078    @XNode("schema")
079    public String schemaName;
080
081    @XNode("idField")
082    public String idField;
083
084    @XNode("table")
085    public String tableName;
086
087    @XNode("readOnly")
088    public Boolean readOnly;
089
090    @XNode("passwordField")
091    public String passwordField;
092
093    @XNode("passwordHashAlgorithm")
094    public String passwordHashAlgorithm;
095
096    @XNodeList(value = "permissions/permission", type = PermissionDescriptor[].class, componentType = PermissionDescriptor.class)
097    public PermissionDescriptor[] permissions;
098
099    @XNode("cacheTimeout")
100    public Integer cacheTimeout;
101
102    @XNode("cacheMaxSize")
103    public Integer cacheMaxSize;
104
105    @XNode("cacheEntryName")
106    public String cacheEntryName;
107
108    @XNode("cacheEntryWithoutReferencesName")
109    public String cacheEntryWithoutReferencesName;
110
111    @XNode("negativeCaching")
112    public Boolean negativeCaching;
113
114    @XNode("substringMatchType")
115    public String substringMatchType;
116
117    public boolean isReadOnly() {
118        return readOnly == null ? READ_ONLY_DEFAULT : readOnly.booleanValue();
119    }
120
121    public void setReadOnly(boolean readOnly) {
122        this.readOnly = Boolean.valueOf(readOnly);
123    }
124
125    public int getCacheTimeout() {
126        return cacheTimeout == null ? CACHE_TIMEOUT_DEFAULT : cacheTimeout.intValue();
127    }
128
129    public int getCacheMaxSize() {
130        return cacheMaxSize == null ? CACHE_MAX_SIZE_DEFAULT : cacheMaxSize.intValue();
131    }
132
133    public SubstringMatchType getSubstringMatchType() {
134        if (StringUtils.isBlank(substringMatchType)) {
135            return SUBSTRING_MATCH_TYPE_DEFAULT;
136        }
137        try {
138            return SubstringMatchType.valueOf(substringMatchType);
139        } catch (IllegalArgumentException  e) {
140            log.error("Unknown value for <substringMatchType>: " + substringMatchType);
141            return SUBSTRING_MATCH_TYPE_DEFAULT;
142        }
143    }
144
145    /**
146     * Sub-classes MUST OVERRIDE and use a more specific return type.
147     * <p>
148     * Usually it's bad to use clone(), and a copy-constructor is preferred, but here we want the copy method to be
149     * inheritable so clone() is appropriate.
150     * <p>
151     * {@inheritDoc}
152     */
153    @Override
154    public BaseDirectoryDescriptor clone() {
155        BaseDirectoryDescriptor clone;
156        try {
157            clone = (BaseDirectoryDescriptor) super.clone();
158        } catch (CloneNotSupportedException e) {
159            throw new AssertionError(e);
160        }
161        // basic fields are already copied by super.clone()
162        if (permissions != null) {
163            clone.permissions = new PermissionDescriptor[permissions.length];
164            for (int i = 0; i < permissions.length; i++) {
165                clone.permissions[i] = permissions[i].clone();
166            }
167        }
168        return clone;
169    }
170
171    public void merge(BaseDirectoryDescriptor other) {
172        template = template || other.template;
173
174        if (other.parentDirectory != null) {
175            parentDirectory = other.parentDirectory;
176        }
177        if (other.schemaName != null) {
178            schemaName = other.schemaName;
179        }
180        if (other.idField != null) {
181            idField = other.idField;
182        }
183        if (other.tableName != null) {
184            tableName = other.tableName;
185        }
186        if (other.readOnly != null) {
187            readOnly = other.readOnly;
188        }
189        if (other.passwordField != null) {
190            passwordField = other.passwordField;
191        }
192        if (other.passwordHashAlgorithm != null) {
193            passwordHashAlgorithm = other.passwordHashAlgorithm;
194        }
195        if (other.permissions != null && other.permissions.length != 0) {
196            permissions = other.permissions;
197        }
198        if (other.cacheTimeout != null) {
199            cacheTimeout = other.cacheTimeout;
200        }
201        if (other.cacheMaxSize != null) {
202            cacheMaxSize = other.cacheMaxSize;
203        }
204        if (other.cacheEntryName != null) {
205            cacheEntryName = other.cacheEntryName;
206        }
207        if (other.cacheEntryWithoutReferencesName != null) {
208            cacheEntryWithoutReferencesName = other.cacheEntryWithoutReferencesName;
209        }
210        if (other.negativeCaching != null) {
211            negativeCaching = other.negativeCaching;
212        }
213        if (other.substringMatchType != null) {
214            substringMatchType = other.substringMatchType;
215        }
216    }
217
218    /**
219     * Creates a new {@link Directory} instance from this {@link DirectoryDescriptor).
220     */
221    public Directory newDirectory() {
222        throw new UnsupportedOperationException("Cannot be instantiated as Directory: " + getClass().getName());
223    }
224
225}