001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.tools.makeldif;
028
029import static org.opends.server.util.LDIFWriter.*;
030import static org.opends.server.util.StaticUtils.*;
031
032import java.io.BufferedWriter;
033import java.io.IOException;
034import java.util.ArrayList;
035import java.util.LinkedHashMap;
036import java.util.List;
037
038import org.forgerock.opendj.ldap.ByteString;
039import org.opends.server.core.DirectoryServer;
040import org.opends.server.types.*;
041import org.opends.server.util.LDIFException;
042
043/**
044 * This class defines an entry that is generated using a MakeLDIF branch or
045 * template.
046 */
047public class TemplateEntry
048{
049  /** The branch used to generate this entry (if it is associated with a branch). */
050  private Branch branch;
051  /** The DN for this template entry, if it is known. */
052  private DN dn;
053  /** The DN of the parent entry for this template entry, if it is available. */
054  private DN parentDN;
055
056  /**
057   * The set of attributes associated with this template entry, mapped from the
058   * lowercase name of the attribute to the list of generated values.
059   */
060  private final LinkedHashMap<AttributeType, ArrayList<TemplateValue>> attributes = new LinkedHashMap<>();
061
062  /** The template used to generate this entry (if it is associated with a template). */
063  private Template template;
064
065
066  /**
067   * Creates a new template entry that will be associated with the provided
068   * branch.
069   *
070   * @param  branch  The branch to use when creating this template entry.
071   */
072  public TemplateEntry(Branch branch)
073  {
074    this.branch = branch;
075
076    dn         = branch.getBranchDN();
077  }
078
079
080
081  /**
082   * Creates a new template entry that will be associated with the provided
083   * template.
084   *
085   * @param  template  The template used to generate this entry.
086   * @param  parentDN  The DN of the parent entry for this template entry.
087   */
088  public TemplateEntry(Template template, DN parentDN)
089  {
090    this.template = template;
091    this.parentDN = parentDN;
092  }
093
094
095
096  /**
097   * Retrieves the branch used to generate this entry.
098   *
099   * @return  The branch used to generate this entry, or <CODE>null</CODE> if it
100   *          is associated with a template instead of a branch.
101   */
102  public Branch getBranch()
103  {
104    return branch;
105  }
106
107
108
109  /**
110   * Retrieves the template used to generate this entry.
111   *
112   * @return  The template used to generate this entry, or <CODE>null</CODE> if
113   *          it is associated with a branch instead of a template.
114   */
115  public Template getTemplate()
116  {
117    return template;
118  }
119
120
121
122  /**
123   * Retrieves the DN of the parent entry for this template entry.
124   *
125   * @return  The DN of the parent entry for this template entry, or
126   *          <CODE>null</CODE> if there is no parent DN.
127   */
128  public DN getParentDN()
129  {
130    return parentDN;
131  }
132
133
134
135  /**
136   * Retrieves the DN for this template entry, if it is known.
137   *
138   * @return  The DN for this template entry if it is known, or
139   *          <CODE>null</CODE> if it cannot yet be determined.
140   */
141  public DN getDN()
142  {
143    if (dn == null)
144    {
145      RDN rdn;
146      AttributeType[] rdnAttrs = template.getRDNAttributes();
147      if (rdnAttrs.length == 1)
148      {
149        AttributeType t = rdnAttrs[0];
150        TemplateValue v = getValue(t);
151        if (v == null)
152        {
153          return null;
154        }
155
156        rdn = new RDN(t, ByteString.valueOfUtf8(v.getValue().toString()));
157      }
158      else
159      {
160        String[]         names  = new String[rdnAttrs.length];
161        ByteString[] values = new ByteString[rdnAttrs.length];
162        for (int i=0; i < rdnAttrs.length; i++)
163        {
164          AttributeType t = rdnAttrs[i];
165          TemplateValue v = getValue(t);
166          if (v == null)
167          {
168            return null;
169          }
170
171          names[i]  = t.getPrimaryName();
172          values[i] = ByteString.valueOfUtf8(v.getValue().toString());
173        }
174
175        rdn = new RDN(rdnAttrs, names, values);
176      }
177
178      dn = parentDN.child(rdn);
179    }
180
181    return dn;
182  }
183
184
185
186  /**
187   * Indicates whether this entry contains one or more values for the specified
188   * attribute type.
189   *
190   * @param  attributeType  The attribute type for which to make the
191   *                        determination.
192   *
193   * @return  <CODE>true</CODE> if this entry contains one or more values for
194   *          the specified attribute type, or <CODE>false</CODE> if not.
195   */
196  public boolean hasAttribute(AttributeType attributeType)
197  {
198    return attributes.containsKey(attributeType);
199  }
200
201
202
203  /**
204   * Retrieves the value for the specified attribute, if defined.  If the
205   * specified attribute has multiple values, then the first will be returned.
206   *
207   * @param  attributeType  The attribute type for which to retrieve the value.
208   *
209   * @return  The value for the specified attribute, or <CODE>null</CODE> if
210   *          there are no values for that attribute type.
211   */
212  public TemplateValue getValue(AttributeType attributeType)
213  {
214    ArrayList<TemplateValue> valueList = attributes.get(attributeType);
215    if (valueList != null && !valueList.isEmpty())
216    {
217      return valueList.get(0);
218    }
219    return null;
220  }
221
222
223
224  /**
225   * Retrieves the set of values for the specified attribute, if defined.
226   *
227   * @param  attributeType  The attribute type for which to retrieve the set of
228   *                        values.
229   *
230   * @return  The set of values for the specified attribute, or
231   *          <CODE>null</CODE> if there are no values for that attribute type.
232   */
233  public List<TemplateValue> getValues(AttributeType attributeType)
234  {
235    return attributes.get(attributeType);
236  }
237
238
239
240  /**
241   * Adds the provided template value to this entry.
242   *
243   * @param  value  The value to add to this entry.
244   */
245  public void addValue(TemplateValue value)
246  {
247    ArrayList<TemplateValue> valueList = attributes.get(value.getAttributeType());
248    if (valueList == null)
249    {
250      valueList = new ArrayList<>();
251      attributes.put(value.getAttributeType(), valueList);
252    }
253    valueList.add(value);
254  }
255
256
257  /**
258   * Writes this entry in LDIF form.  No filtering will be
259   * performed for this entry, nor will any export plugins be invoked.
260   *
261   * @param  exportConfig  The configuration that specifies how the
262   *                       entry should be written.
263   *
264   * @return  <CODE>true</CODE> if the entry is actually written, or
265   *          <CODE>false</CODE> if it is not for some reason.
266   *
267   * @throws  IOException  If a problem occurs while writing the
268   *                       information.
269   *
270   * @throws  LDIFException  If a problem occurs while trying to
271   *                         determine whether to write the entry.
272   */
273  public boolean toLDIF(LDIFExportConfig exportConfig)
274         throws IOException, LDIFException
275  {
276    // Process all of the attributes for this entry.
277    LinkedHashMap<ObjectClass,String> objectClasses = new LinkedHashMap<>();
278    LinkedHashMap<AttributeType,List<Attribute>> userAttributes = new LinkedHashMap<>();
279    LinkedHashMap<AttributeType,List<Attribute>> operationalAttributes = new LinkedHashMap<>();
280    LinkedHashMap<AttributeType, List<Attribute>> urlAttributes = new LinkedHashMap<>();
281    LinkedHashMap<AttributeType, List<Attribute>> base64Attributes = new LinkedHashMap<>();
282
283    for (AttributeType t : attributes.keySet())
284    {
285      ArrayList<TemplateValue> valueList = attributes.get(t);
286      if (t.isObjectClass())
287      {
288        for (TemplateValue v : valueList)
289        {
290          String ocName = toLowerCase(v.getValue().toString());
291          ObjectClass oc = DirectoryServer.getObjectClass(ocName, true);
292          objectClasses.put(oc, ocName);
293        }
294      }
295      else if (t.isOperational())
296      {
297        AttributeBuilder builder = new AttributeBuilder(t, t.getNameOrOID());
298        for (TemplateValue v : valueList)
299        {
300          builder.add(v.getValue().toString());
301        }
302
303        operationalAttributes.put(t, builder.toAttributeList());
304      }
305      else
306      {
307        AttributeBuilder builder = new AttributeBuilder(t, t.getNameOrOID());
308        AttributeBuilder urlBuilder = null;
309        AttributeBuilder base64Builder = null;
310        for (TemplateValue v : valueList)
311        {
312          ByteString value = ByteString.valueOfUtf8(v.getValue().toString());
313          builder.add(value);
314          if (v.getTemplateLine().isURL())
315          {
316            if (urlBuilder == null)
317            {
318              urlBuilder = new AttributeBuilder(t, t.getNameOrOID());
319            }
320            urlBuilder.add(value);
321          }
322          else if (v.getTemplateLine().isBase64())
323          {
324            if (base64Builder == null)
325            {
326              base64Builder = new AttributeBuilder(t, t.getNameOrOID());
327            }
328            base64Builder.add(value);
329          }
330        }
331
332        userAttributes.put(t, builder.toAttributeList());
333        if (urlBuilder != null)
334        {
335          urlAttributes.put(t, urlBuilder.toAttributeList());
336        }
337        if (base64Builder != null)
338        {
339          base64Attributes.put(t, base64Builder.toAttributeList());
340        }
341      }
342    }
343
344    // Get the information necessary to write the LDIF.
345    BufferedWriter writer     = exportConfig.getWriter();
346    int            wrapColumn = exportConfig.getWrapColumn();
347    boolean        wrapLines  = wrapColumn > 1;
348
349
350    // First, write the DN.  It will always be included.
351    StringBuilder dnLine = new StringBuilder("dn");
352    appendLDIFSeparatorAndValue(dnLine,
353        ByteString.valueOfUtf8(getDN().toString()));
354    writeLDIFLine(dnLine, writer, wrapLines, wrapColumn);
355
356
357    // Next, the set of objectclasses.
358    final boolean typesOnly = exportConfig.typesOnly();
359    if (exportConfig.includeObjectClasses())
360    {
361      if (typesOnly)
362      {
363        StringBuilder ocLine = new StringBuilder("objectClass:");
364        writeLDIFLine(ocLine, writer, wrapLines, wrapColumn);
365      }
366      else
367      {
368        for (String s : objectClasses.values())
369        {
370          StringBuilder ocLine = new StringBuilder("objectClass: ").append(s);
371          writeLDIFLine(ocLine, writer, wrapLines, wrapColumn);
372        }
373      }
374    }
375
376
377    // Now the set of user attributes.
378    for (AttributeType attrType : userAttributes.keySet())
379    {
380      if (exportConfig.includeAttribute(attrType))
381      {
382        for (Attribute a : userAttributes.get(attrType))
383        {
384          if (a.isVirtual() && !exportConfig.includeVirtualAttributes())
385          {
386            continue;
387          }
388
389          StringBuilder attrName = attrNameWithOptions(a);
390          if (typesOnly)
391          {
392            attrName.append(":");
393
394            writeLDIFLine(attrName, writer, wrapLines, wrapColumn);
395          }
396          else
397          {
398            List<Attribute> urlAttrList = urlAttributes.get(attrType);
399            List<Attribute> base64AttrList = base64Attributes.get(attrType);
400
401            for (ByteString v : a)
402            {
403              StringBuilder attrLine = new StringBuilder(attrName);
404              boolean isURLValue = contains(urlAttrList, v);
405              boolean isBase64Value = contains(base64AttrList, v);
406              appendLDIFSeparatorAndValue(attrLine,
407                                          v,
408                                          isURLValue,
409                                          isBase64Value);
410              writeLDIFLine(attrLine, writer, wrapLines, wrapColumn);
411            }
412          }
413        }
414      }
415    }
416
417
418    // Next, the set of operational attributes.
419    if (exportConfig.includeOperationalAttributes())
420    {
421      for (AttributeType attrType : operationalAttributes.keySet())
422      {
423        if (exportConfig.includeAttribute(attrType))
424        {
425          for (Attribute a : operationalAttributes.get(attrType))
426          {
427            if (a.isVirtual() && !exportConfig.includeVirtualAttributes())
428            {
429              continue;
430            }
431
432            StringBuilder attrName = attrNameWithOptions(a);
433            if (typesOnly)
434            {
435              attrName.append(":");
436
437              writeLDIFLine(attrName, writer, wrapLines, wrapColumn);
438            }
439            else
440            {
441              for (ByteString v : a)
442              {
443                StringBuilder attrLine = new StringBuilder(attrName);
444                appendLDIFSeparatorAndValue(attrLine, v);
445                writeLDIFLine(attrLine, writer, wrapLines, wrapColumn);
446              }
447            }
448          }
449        }
450      }
451    }
452
453    // Make sure there is a blank line after the entry.
454    writer.newLine();
455
456    return true;
457  }
458
459  private StringBuilder attrNameWithOptions(Attribute a)
460  {
461    StringBuilder attrName = new StringBuilder(a.getName());
462    for (String o : a.getOptions())
463    {
464      attrName.append(";");
465      attrName.append(o);
466    }
467    return attrName;
468  }
469
470  private boolean contains(List<Attribute> urlAttrList, ByteString v)
471  {
472    if (urlAttrList != null)
473    {
474      for (Attribute urlAttr : urlAttrList)
475      {
476        for (ByteString urlValue : urlAttr)
477        {
478          if (urlValue.equals(v))
479          {
480            return true;
481          }
482        }
483      }
484    }
485    return false;
486  }
487}