001 /*
002 * Copyright 2009-2015 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2015 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk.unboundidds.controls;
022
023
024
025 import java.util.ArrayList;
026 import java.util.Collection;
027 import java.util.Collections;
028 import java.util.Iterator;
029 import java.util.List;
030
031 import com.unboundid.asn1.ASN1Element;
032 import com.unboundid.asn1.ASN1OctetString;
033 import com.unboundid.asn1.ASN1Sequence;
034 import com.unboundid.ldap.sdk.Attribute;
035 import com.unboundid.ldap.sdk.Entry;
036 import com.unboundid.ldap.sdk.LDAPException;
037 import com.unboundid.ldap.sdk.ReadOnlyEntry;
038 import com.unboundid.ldap.sdk.ResultCode;
039 import com.unboundid.util.NotMutable;
040 import com.unboundid.util.ThreadSafety;
041 import com.unboundid.util.ThreadSafetyLevel;
042
043 import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
044 import static com.unboundid.util.Debug.*;
045 import static com.unboundid.util.StaticUtils.*;
046
047
048
049 /**
050 * <BLOCKQUOTE>
051 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID
052 * LDAP SDK for Java. It is not available for use in applications that
053 * include only the Standard Edition of the LDAP SDK, and is not supported for
054 * use in conjunction with non-UnboundID products.
055 * </BLOCKQUOTE>
056 * This class provides a joined entry, which is a read-only representation of an
057 * entry that has been joined with a search result entry using the LDAP join
058 * control. See the class-level documentation for the
059 * {@link JoinRequestControl} class for additional information and an example
060 * demonstrating its use.
061 * <BR><BR>
062 * Joined entries are encoded as follows:
063 * <PRE>
064 * JoinedEntry ::= SEQUENCE {
065 * objectName LDAPDN,
066 * attributes PartialAttributeList,
067 * nestedJoinResults SEQUENCE OF JoinedEntry OPTIONAL }
068 * </PRE>
069 */
070 @NotMutable()
071 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
072 public final class JoinedEntry
073 extends ReadOnlyEntry
074 {
075 /**
076 * The serial version UID for this serializable class.
077 */
078 private static final long serialVersionUID = -6519864521813773703L;
079
080
081
082 // The list of nested join results for this joined entry.
083 private final List<JoinedEntry> nestedJoinResults;
084
085
086
087 /**
088 * Creates a new joined entry with the specified DN, attributes, and nested
089 * join results.
090 *
091 * @param entry The entry containing the DN and attributes to
092 * use for this joined entry. It must not be
093 * {@code null}.
094 * @param nestedJoinResults A list of nested join results for this joined
095 * entry. It may be {@code null} or empty if there
096 * are no nested join results.
097 */
098 public JoinedEntry(final Entry entry,
099 final List<JoinedEntry> nestedJoinResults)
100 {
101 this(entry.getDN(), entry.getAttributes(), nestedJoinResults);
102 }
103
104
105
106 /**
107 * Creates a new joined entry with the specified DN, attributes, and nested
108 * join results.
109 *
110 * @param dn The DN for this joined entry. It must not be
111 * {@code null}.
112 * @param attributes The set of attributes for this joined entry. It
113 * must not be {@code null}.
114 * @param nestedJoinResults A list of nested join results for this joined
115 * entry. It may be {@code null} or empty if there
116 * are no nested join results.
117 */
118 public JoinedEntry(final String dn, final Collection<Attribute> attributes,
119 final List<JoinedEntry> nestedJoinResults)
120 {
121 super(dn, attributes);
122
123 if (nestedJoinResults == null)
124 {
125 this.nestedJoinResults = Collections.emptyList();
126 }
127 else
128 {
129 this.nestedJoinResults = Collections.unmodifiableList(nestedJoinResults);
130 }
131 }
132
133
134
135 /**
136 * Encodes this joined entry to an ASN.1 element.
137 *
138 * @return An ASN.1 element containing the encoded representation of this
139 * joined entry.
140 */
141 ASN1Element encode()
142 {
143 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3);
144
145 elements.add(new ASN1OctetString(getDN()));
146
147 final ArrayList<ASN1Element> attrElements = new ArrayList<ASN1Element>();
148 for (final Attribute a : getAttributes())
149 {
150 attrElements.add(a.encode());
151 }
152 elements.add(new ASN1Sequence(attrElements));
153
154 if (! nestedJoinResults.isEmpty())
155 {
156 final ArrayList<ASN1Element> nestedElements =
157 new ArrayList<ASN1Element>(nestedJoinResults.size());
158 for (final JoinedEntry je : nestedJoinResults)
159 {
160 nestedElements.add(je.encode());
161 }
162 elements.add(new ASN1Sequence(nestedElements));
163 }
164
165 return new ASN1Sequence(elements);
166 }
167
168
169
170 /**
171 * Decodes the provided ASN.1 element as a joined entry.
172 *
173 * @param element The ASN.1 element to decode as a joined entry.
174 *
175 * @return The decoded joined entry.
176 *
177 * @throws LDAPException If a problem occurs while attempting to decode the
178 * provided ASN.1 element as a joined entry.
179 */
180 static JoinedEntry decode(final ASN1Element element)
181 throws LDAPException
182 {
183 try
184 {
185 final ASN1Element[] elements =
186 ASN1Sequence.decodeAsSequence(element).elements();
187 final String dn =
188 ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
189
190 final ASN1Element[] attrElements =
191 ASN1Sequence.decodeAsSequence(elements[1]).elements();
192 final ArrayList<Attribute> attrs =
193 new ArrayList<Attribute>(attrElements.length);
194 for (final ASN1Element e : attrElements)
195 {
196 attrs.add(Attribute.decode(ASN1Sequence.decodeAsSequence(e)));
197 }
198
199 final ArrayList<JoinedEntry> nestedJoinResults;
200 if (elements.length == 3)
201 {
202 final ASN1Element[] nestedElements =
203 ASN1Sequence.decodeAsSequence(elements[2]).elements();
204 nestedJoinResults = new ArrayList<JoinedEntry>(nestedElements.length);
205 for (final ASN1Element e : nestedElements)
206 {
207 nestedJoinResults.add(decode(e));
208 }
209 }
210 else
211 {
212 nestedJoinResults = new ArrayList<JoinedEntry>(0);
213 }
214
215 return new JoinedEntry(dn, attrs, nestedJoinResults);
216 }
217 catch (Exception e)
218 {
219 debugException(e);
220
221 throw new LDAPException(ResultCode.DECODING_ERROR,
222 ERR_JOINED_ENTRY_CANNOT_DECODE.get(getExceptionMessage(e)), e);
223 }
224 }
225
226
227
228 /**
229 * Retrieves the list of nested join results for this joined entry.
230 *
231 * @return The list of nested join results for this joined entry, or an
232 * empty list if there are none.
233 */
234 public List<JoinedEntry> getNestedJoinResults()
235 {
236 return nestedJoinResults;
237 }
238
239
240
241 /**
242 * Appends a string representation of this joined entry to the provided
243 * buffer.
244 *
245 * @param buffer The buffer to which the information should be appended.
246 */
247 @Override()
248 public void toString(final StringBuilder buffer)
249 {
250 buffer.append("JoinedEntry(dn='");
251 buffer.append(getDN());
252 buffer.append("', attributes={");
253
254 final Iterator<Attribute> attrIterator = getAttributes().iterator();
255 while (attrIterator.hasNext())
256 {
257 attrIterator.next().toString(buffer);
258 if (attrIterator.hasNext())
259 {
260 buffer.append(", ");
261 }
262 }
263
264 buffer.append("}, nestedJoinResults={");
265
266 final Iterator<JoinedEntry> entryIterator = nestedJoinResults.iterator();
267 while (entryIterator.hasNext())
268 {
269 entryIterator.next().toString(buffer);
270 if (entryIterator.hasNext())
271 {
272 buffer.append(", ");
273 }
274 }
275
276 buffer.append("})");
277 }
278 }