001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015-2016 ForgeRock AS.
015 */
016package org.opends.server.schema;
017
018import static org.opends.server.util.ServerConstants.*;
019
020import java.util.Arrays;
021import java.util.List;
022import java.util.Map;
023
024import org.forgerock.opendj.ldap.schema.AttributeType;
025import org.forgerock.opendj.ldap.schema.Schema;
026import org.forgerock.opendj.ldap.schema.SchemaBuilder;
027import org.opends.server.config.ConfigConstants;
028import org.opends.server.core.ServerContext;
029import org.opends.server.types.CommonSchemaElements;
030import org.opends.server.types.ObjectClass;
031import org.opends.server.types.SchemaFileElement;
032import org.opends.server.util.RemoveOnceSDKSchemaIsUsed;
033import org.opends.server.util.ServerConstants;
034
035/**
036 * Represents a schema element which is either a SDK attribute type or an objectclass from the server.
037 * <p>
038 * In absence of a common interface, this class allows to process all elements in the same way,
039 * and to provide useful server-oriented methods like {@code getSchemaFile()} or
040 * {@code getOrigin()}.
041 */
042@RemoveOnceSDKSchemaIsUsed("This class is a temporary mechanism"
043    + " to manage in the same way SDK and server schema element classes")
044public class SomeSchemaElement implements SchemaFileElement
045{
046  private final ObjectClass objectClass;
047  private AttributeType attributeType;
048
049  /**
050   * Builds SomeSchemaElement.
051   *
052   * @param objectClass
053   *          the common schema element to wrap
054   */
055  public SomeSchemaElement(ObjectClass objectClass)
056  {
057    this.objectClass = objectClass;
058    this.attributeType = null;
059  }
060
061  /**
062   * Builds SomeSchemaElement.
063   *
064   * @param attributeType
065   *          the attribute type element to wrap
066   */
067  public SomeSchemaElement(AttributeType attributeType)
068  {
069    this.objectClass = null;
070    this.attributeType = attributeType;
071  }
072
073  /**
074   * Returns the wrapped schema element as an object class.
075   *
076   * @return the wrapped object class
077   */
078  public ObjectClass getObjectClass()
079  {
080    return objectClass;
081  }
082
083  /**
084   * Returns the wrapped schema element as an attribute type.
085   *
086   * @return the wrapped attribute type
087   */
088  public AttributeType getAttributeType()
089  {
090    return attributeType;
091  }
092
093  /**
094   * Returns whether the wrapped element is an attribute type.
095   *
096   * @return {@code true} when the wrapped element is an attribute type, {@code false} otherwise
097   */
098  public boolean isAttributeType()
099  {
100    return attributeType != null;
101  }
102
103  /**
104   * Returns whether the wrapped element is an object class.
105   *
106   * @return {@code true} when the wrapped element is an object class, {@code false} otherwise
107   */
108  public boolean isObjectClass()
109  {
110    return objectClass != null;
111  }
112
113  /**
114   * Returns the OID of the wrapped element.
115   *
116   * @return the OID of the wrapped element.
117   */
118  public String getOID()
119  {
120    return attributeType != null ? attributeType.getOID() : objectClass.getOID();
121  }
122
123  /**
124   * Returns the name or OID of the wrapped element.
125   *
126   * @return the name or OID of the wrapped element.
127   */
128  public String getNameOrOID()
129  {
130    return attributeType != null ? attributeType.getNameOrOID() : objectClass.getNameOrOID();
131  }
132
133  /**
134   * Returns the names of the wrapped element.
135   *
136   * @return the names of the wrapped element.
137   */
138  public Iterable<String> getNames()
139  {
140    return attributeType != null ? attributeType.getNames() : objectClass.getNormalizedNames();
141  }
142
143  @Override
144  public Map<String, List<String>> getExtraProperties()
145  {
146    return attributeType != null ? attributeType.getExtraProperties() : objectClass.getExtraProperties();
147  }
148
149  @Override
150  public String toString()
151  {
152    return attributeType != null ? attributeType.toString() : objectClass.toString();
153  }
154
155  /**
156   * Retrieves the definition string used to create this attribute
157   * type and including the X-SCHEMA-FILE extension.
158   *
159   * @return  The definition string used to create this attribute
160   *          type including the X-SCHEMA-FILE extension.
161   */
162  public String getDefinitionWithFileName()
163  {
164    final String schemaFile = getSchemaFile();
165    final String definition = toString();
166    if (schemaFile != null)
167    {
168      int pos = definition.lastIndexOf(')');
169      return definition.substring(0, pos).trim() + " " + SCHEMA_PROPERTY_FILENAME + " '" + schemaFile + "' )";
170    }
171    return definition;
172  }
173
174  /**
175   * Returns the name of the schema file that contains the definition of the wrapped element.
176   *
177   * @return the name of the schema file that contains the definition of the wrapped element.
178   */
179  public String getSchemaFile()
180  {
181    return getExtraPropertySingleValue(ServerConstants.SCHEMA_PROPERTY_FILENAME);
182  }
183
184  /**
185   * Sets the name of the schema file that contains the definition of the wrapped element.
186   *
187   * @param serverContext
188   *          the server context
189   * @param schemaFile
190   *          the name of the schema file that contains the definition of the wrapped element.
191   */
192  public void setSchemaFile(ServerContext serverContext, String schemaFile)
193  {
194    setExtraPropertySingleValue(serverContext, SCHEMA_PROPERTY_FILENAME, schemaFile);
195  }
196
197  /**
198   * Returns the origin of the provided schema element.
199   * @return the origin of the provided schema element.
200   */
201  public String getOrigin()
202  {
203    return getExtraPropertySingleValue(ServerConstants.SCHEMA_PROPERTY_ORIGIN);
204  }
205
206  private String getExtraPropertySingleValue(String schemaPropertyOrigin)
207  {
208    if (objectClass != null)
209    {
210      return CommonSchemaElements.getSingleValueProperty(objectClass, schemaPropertyOrigin);
211    }
212    List<String> values = attributeType.getExtraProperties().get(schemaPropertyOrigin);
213    return values != null && !values.isEmpty() ? values.get(0) : null;
214  }
215
216  /**
217   * Returns the attribute name of the wrapped element.
218   * @return the attribute name of the wrapped element.
219   */
220  public String getAttributeName()
221  {
222    return attributeType!= null ? ConfigConstants.ATTR_ATTRIBUTE_TYPES : ConfigConstants.ATTR_OBJECTCLASSES;
223  }
224
225  /**
226   * Sets a single-valued extra property on the wrapped element.
227   *
228   * @param serverContext
229   *          the server context
230   * @param property
231   *          the property to set
232   * @param value
233   *          the value to set
234   */
235  public void setExtraPropertySingleValue(ServerContext serverContext, String property, String value)
236  {
237    if (attributeType != null)
238    {
239      List<String> values = value != null ?  Arrays.asList(value) : null;
240      setExtraPropertyMultipleValues(serverContext, property, values);
241    }
242    else
243    {
244      CommonSchemaElements.setExtraProperty(objectClass, property, value);
245    }
246  }
247
248  /**
249   * Sets a multi-valued extra property on the wrapped element.
250   *
251   * @param serverContext
252   *          the server context
253   * @param property
254   *          the property to set
255   * @param values
256   *          the values to set
257   */
258  public void setExtraPropertyMultipleValues(ServerContext serverContext, String property, List<String> values)
259  {
260    if (attributeType != null)
261    {
262      SchemaBuilder schemaBuilder = serverContext != null ?
263          new SchemaBuilder(serverContext.getSchemaNG()) : new SchemaBuilder(Schema.getDefaultSchema());
264      AttributeType.Builder builder =
265          schemaBuilder.buildAttributeType(attributeType).removeExtraProperty(property, (String) null);
266      if (values != null  && !values.isEmpty())
267      {
268        builder.extraProperties(property, values);
269      }
270      attributeType = builder.addToSchemaOverwrite().toSchema().getAttributeType(attributeType.getNameOrOID());
271    }
272    else
273    {
274      objectClass.setExtraProperty(property, values);
275    }
276  }
277
278  /**
279   * Returns a copy of the provided attribute type, changing the superior attribute type.
280   *
281   * @param attributeType
282   *          the attribute type for which a modified copy must be built
283   * @param newSuperiorType
284   *          the new superior attribute type to set, {@code null} means remove the superior type
285   * @return an attribute type builder to build an updated copy of the provided attribute type
286   */
287  public static AttributeType changeSuperiorType(AttributeType attributeType, AttributeType newSuperiorType)
288  {
289    String superiorTypeOID = newSuperiorType != null ? newSuperiorType.getNameOrOID() : null;
290    Schema schema = new SchemaBuilder()
291      .buildAttributeType(attributeType)
292      .superiorType(superiorTypeOID)
293      .addToSchemaOverwrite()
294      .toSchema();
295    return schema.getAttributeType(attributeType.getNameOrOID());
296  }
297}