Shuyi Chen | d7955ce | 2013-05-22 14:51:55 -0700 | [diff] [blame] | 1 | /** |
| 2 | * $RCSfile$ |
| 3 | * $Revision$ |
| 4 | * $Date$ |
| 5 | * |
| 6 | * Copyright 2009 Robin Collier. |
| 7 | * |
| 8 | * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); |
| 9 | * you may not use this file except in compliance with the License. |
| 10 | * You may obtain a copy of the License at |
| 11 | * |
| 12 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 13 | * |
| 14 | * Unless required by applicable law or agreed to in writing, software |
| 15 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 17 | * See the License for the specific language governing permissions and |
| 18 | * limitations under the License. |
| 19 | */ |
| 20 | package org.jivesoftware.smackx.pubsub;
|
| 21 |
|
| 22 | import java.util.ArrayList;
|
| 23 | import java.util.Collection;
|
| 24 | import java.util.Iterator;
|
| 25 | import java.util.List;
|
| 26 | import java.util.concurrent.ConcurrentHashMap;
|
| 27 |
|
| 28 | import org.jivesoftware.smack.PacketListener;
|
| 29 | import org.jivesoftware.smack.Connection;
|
| 30 | import org.jivesoftware.smack.XMPPException;
|
| 31 | import org.jivesoftware.smack.filter.OrFilter;
|
| 32 | import org.jivesoftware.smack.filter.PacketFilter;
|
| 33 | import org.jivesoftware.smack.packet.Message;
|
| 34 | import org.jivesoftware.smack.packet.Packet;
|
| 35 | import org.jivesoftware.smack.packet.PacketExtension;
|
| 36 | import org.jivesoftware.smack.packet.IQ.Type;
|
| 37 | import org.jivesoftware.smackx.Form;
|
| 38 | import org.jivesoftware.smackx.packet.DelayInformation;
|
| 39 | import org.jivesoftware.smackx.packet.DiscoverInfo;
|
| 40 | import org.jivesoftware.smackx.packet.Header;
|
| 41 | import org.jivesoftware.smackx.packet.HeadersExtension;
|
| 42 | import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener;
|
| 43 | import org.jivesoftware.smackx.pubsub.listener.ItemEventListener;
|
| 44 | import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener;
|
| 45 | import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
| 46 | import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
|
| 47 | import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend;
|
| 48 | import org.jivesoftware.smackx.pubsub.util.NodeUtils;
|
| 49 |
|
| 50 | abstract public class Node
|
| 51 | {
|
| 52 | protected Connection con;
|
| 53 | protected String id;
|
| 54 | protected String to;
|
| 55 |
|
| 56 | protected ConcurrentHashMap<ItemEventListener<Item>, PacketListener> itemEventToListenerMap = new ConcurrentHashMap<ItemEventListener<Item>, PacketListener>();
|
| 57 | protected ConcurrentHashMap<ItemDeleteListener, PacketListener> itemDeleteToListenerMap = new ConcurrentHashMap<ItemDeleteListener, PacketListener>();
|
| 58 | protected ConcurrentHashMap<NodeConfigListener, PacketListener> configEventToListenerMap = new ConcurrentHashMap<NodeConfigListener, PacketListener>();
|
| 59 |
|
| 60 | /**
|
| 61 | * Construct a node associated to the supplied connection with the specified
|
| 62 | * node id.
|
| 63 | *
|
| 64 | * @param connection The connection the node is associated with
|
| 65 | * @param nodeName The node id
|
| 66 | */
|
| 67 | Node(Connection connection, String nodeName)
|
| 68 | {
|
| 69 | con = connection;
|
| 70 | id = nodeName;
|
| 71 | }
|
| 72 |
|
| 73 | /**
|
| 74 | * Some XMPP servers may require a specific service to be addressed on the
|
| 75 | * server.
|
| 76 | *
|
| 77 | * For example, OpenFire requires the server to be prefixed by <b>pubsub</b>
|
| 78 | */
|
| 79 | void setTo(String toAddress)
|
| 80 | {
|
| 81 | to = toAddress;
|
| 82 | }
|
| 83 |
|
| 84 | /**
|
| 85 | * Get the NodeId
|
| 86 | *
|
| 87 | * @return the node id
|
| 88 | */
|
| 89 | public String getId()
|
| 90 | {
|
| 91 | return id;
|
| 92 | }
|
| 93 | /**
|
| 94 | * Returns a configuration form, from which you can create an answer form to be submitted
|
| 95 | * via the {@link #sendConfigurationForm(Form)}.
|
| 96 | *
|
| 97 | * @return the configuration form
|
| 98 | */
|
| 99 | public ConfigureForm getNodeConfiguration()
|
| 100 | throws XMPPException
|
| 101 | {
|
| 102 | Packet reply = sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.CONFIGURE_OWNER, getId()), PubSubNamespace.OWNER);
|
| 103 | return NodeUtils.getFormFromPacket(reply, PubSubElementType.CONFIGURE_OWNER);
|
| 104 | }
|
| 105 |
|
| 106 | /**
|
| 107 | * Update the configuration with the contents of the new {@link Form}
|
| 108 | *
|
| 109 | * @param submitForm
|
| 110 | */
|
| 111 | public void sendConfigurationForm(Form submitForm)
|
| 112 | throws XMPPException
|
| 113 | {
|
| 114 | PubSub packet = createPubsubPacket(Type.SET, new FormNode(FormNodeType.CONFIGURE_OWNER, getId(), submitForm), PubSubNamespace.OWNER);
|
| 115 | SyncPacketSend.getReply(con, packet);
|
| 116 | }
|
| 117 |
|
| 118 | /**
|
| 119 | * Discover node information in standard {@link DiscoverInfo} format.
|
| 120 | *
|
| 121 | * @return The discovery information about the node.
|
| 122 | *
|
| 123 | * @throws XMPPException
|
| 124 | */
|
| 125 | public DiscoverInfo discoverInfo()
|
| 126 | throws XMPPException
|
| 127 | {
|
| 128 | DiscoverInfo info = new DiscoverInfo();
|
| 129 | info.setTo(to);
|
| 130 | info.setNode(getId());
|
| 131 | return (DiscoverInfo)SyncPacketSend.getReply(con, info);
|
| 132 | }
|
| 133 |
|
| 134 | /**
|
| 135 | * Get the subscriptions currently associated with this node.
|
| 136 | *
|
| 137 | * @return List of {@link Subscription}
|
| 138 | *
|
| 139 | * @throws XMPPException
|
| 140 | */
|
| 141 | public List<Subscription> getSubscriptions()
|
| 142 | throws XMPPException
|
| 143 | {
|
| 144 | PubSub reply = (PubSub)sendPubsubPacket(Type.GET, new NodeExtension(PubSubElementType.SUBSCRIPTIONS, getId()));
|
| 145 | SubscriptionsExtension subElem = (SubscriptionsExtension)reply.getExtension(PubSubElementType.SUBSCRIPTIONS);
|
| 146 | return subElem.getSubscriptions();
|
| 147 | }
|
| 148 |
|
| 149 | /**
|
| 150 | * The user subscribes to the node using the supplied jid. The
|
| 151 | * bare jid portion of this one must match the jid for the connection.
|
| 152 | *
|
| 153 | * Please note that the {@link Subscription.State} should be checked
|
| 154 | * on return since more actions may be required by the caller.
|
| 155 | * {@link Subscription.State#pending} - The owner must approve the subscription
|
| 156 | * request before messages will be received.
|
| 157 | * {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true,
|
| 158 | * the caller must configure the subscription before messages will be received. If it is false
|
| 159 | * the caller can configure it but is not required to do so.
|
| 160 | * @param jid The jid to subscribe as.
|
| 161 | * @return The subscription
|
| 162 | * @exception XMPPException
|
| 163 | */
|
| 164 | public Subscription subscribe(String jid)
|
| 165 | throws XMPPException
|
| 166 | {
|
| 167 | PubSub reply = (PubSub)sendPubsubPacket(Type.SET, new SubscribeExtension(jid, getId()));
|
| 168 | return (Subscription)reply.getExtension(PubSubElementType.SUBSCRIPTION);
|
| 169 | }
|
| 170 |
|
| 171 | /**
|
| 172 | * The user subscribes to the node using the supplied jid and subscription
|
| 173 | * options. The bare jid portion of this one must match the jid for the
|
| 174 | * connection.
|
| 175 | *
|
| 176 | * Please note that the {@link Subscription.State} should be checked
|
| 177 | * on return since more actions may be required by the caller.
|
| 178 | * {@link Subscription.State#pending} - The owner must approve the subscription
|
| 179 | * request before messages will be received.
|
| 180 | * {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true,
|
| 181 | * the caller must configure the subscription before messages will be received. If it is false
|
| 182 | * the caller can configure it but is not required to do so.
|
| 183 | * @param jid The jid to subscribe as.
|
| 184 | * @return The subscription
|
| 185 | * @exception XMPPException
|
| 186 | */
|
| 187 | public Subscription subscribe(String jid, SubscribeForm subForm)
|
| 188 | throws XMPPException
|
| 189 | {
|
| 190 | PubSub request = createPubsubPacket(Type.SET, new SubscribeExtension(jid, getId()));
|
| 191 | request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm));
|
| 192 | PubSub reply = (PubSub)PubSubManager.sendPubsubPacket(con, jid, Type.SET, request);
|
| 193 | return (Subscription)reply.getExtension(PubSubElementType.SUBSCRIPTION);
|
| 194 | }
|
| 195 |
|
| 196 | /**
|
| 197 | * Remove the subscription related to the specified JID. This will only
|
| 198 | * work if there is only 1 subscription. If there are multiple subscriptions,
|
| 199 | * use {@link #unsubscribe(String, String)}.
|
| 200 | *
|
| 201 | * @param jid The JID used to subscribe to the node
|
| 202 | *
|
| 203 | * @throws XMPPException
|
| 204 | */
|
| 205 | public void unsubscribe(String jid)
|
| 206 | throws XMPPException
|
| 207 | {
|
| 208 | unsubscribe(jid, null);
|
| 209 | }
|
| 210 |
|
| 211 | /**
|
| 212 | * Remove the specific subscription related to the specified JID.
|
| 213 | *
|
| 214 | * @param jid The JID used to subscribe to the node
|
| 215 | * @param subscriptionId The id of the subscription being removed
|
| 216 | *
|
| 217 | * @throws XMPPException
|
| 218 | */
|
| 219 | public void unsubscribe(String jid, String subscriptionId)
|
| 220 | throws XMPPException
|
| 221 | {
|
| 222 | sendPubsubPacket(Type.SET, new UnsubscribeExtension(jid, getId(), subscriptionId));
|
| 223 | }
|
| 224 |
|
| 225 | /**
|
| 226 | * Returns a SubscribeForm for subscriptions, from which you can create an answer form to be submitted
|
| 227 | * via the {@link #sendConfigurationForm(Form)}.
|
| 228 | *
|
| 229 | * @return A subscription options form
|
| 230 | *
|
| 231 | * @throws XMPPException
|
| 232 | */
|
| 233 | public SubscribeForm getSubscriptionOptions(String jid)
|
| 234 | throws XMPPException
|
| 235 | {
|
| 236 | return getSubscriptionOptions(jid, null);
|
| 237 | }
|
| 238 |
|
| 239 |
|
| 240 | /**
|
| 241 | * Get the options for configuring the specified subscription.
|
| 242 | *
|
| 243 | * @param jid JID the subscription is registered under
|
| 244 | * @param subscriptionId The subscription id
|
| 245 | *
|
| 246 | * @return The subscription option form
|
| 247 | *
|
| 248 | * @throws XMPPException
|
| 249 | */
|
| 250 | public SubscribeForm getSubscriptionOptions(String jid, String subscriptionId)
|
| 251 | throws XMPPException
|
| 252 | {
|
| 253 | PubSub packet = (PubSub)sendPubsubPacket(Type.GET, new OptionsExtension(jid, getId(), subscriptionId));
|
| 254 | FormNode ext = (FormNode)packet.getExtension(PubSubElementType.OPTIONS);
|
| 255 | return new SubscribeForm(ext.getForm());
|
| 256 | }
|
| 257 |
|
| 258 | /**
|
| 259 | * Register a listener for item publication events. This
|
| 260 | * listener will get called whenever an item is published to
|
| 261 | * this node.
|
| 262 | *
|
| 263 | * @param listener The handler for the event
|
| 264 | */
|
| 265 | public void addItemEventListener(ItemEventListener listener)
|
| 266 | {
|
| 267 | PacketListener conListener = new ItemEventTranslator(listener);
|
| 268 | itemEventToListenerMap.put(listener, conListener);
|
| 269 | con.addPacketListener(conListener, new EventContentFilter(EventElementType.items.toString(), "item"));
|
| 270 | }
|
| 271 |
|
| 272 | /**
|
| 273 | * Unregister a listener for publication events.
|
| 274 | *
|
| 275 | * @param listener The handler to unregister
|
| 276 | */
|
| 277 | public void removeItemEventListener(ItemEventListener listener)
|
| 278 | {
|
| 279 | PacketListener conListener = itemEventToListenerMap.remove(listener);
|
| 280 |
|
| 281 | if (conListener != null)
|
| 282 | con.removePacketListener(conListener);
|
| 283 | }
|
| 284 |
|
| 285 | /**
|
| 286 | * Register a listener for configuration events. This listener
|
| 287 | * will get called whenever the node's configuration changes.
|
| 288 | *
|
| 289 | * @param listener The handler for the event
|
| 290 | */
|
| 291 | public void addConfigurationListener(NodeConfigListener listener)
|
| 292 | {
|
| 293 | PacketListener conListener = new NodeConfigTranslator(listener);
|
| 294 | configEventToListenerMap.put(listener, conListener);
|
| 295 | con.addPacketListener(conListener, new EventContentFilter(EventElementType.configuration.toString()));
|
| 296 | }
|
| 297 |
|
| 298 | /**
|
| 299 | * Unregister a listener for configuration events.
|
| 300 | *
|
| 301 | * @param listener The handler to unregister
|
| 302 | */
|
| 303 | public void removeConfigurationListener(NodeConfigListener listener)
|
| 304 | {
|
| 305 | PacketListener conListener = configEventToListenerMap .remove(listener);
|
| 306 |
|
| 307 | if (conListener != null)
|
| 308 | con.removePacketListener(conListener);
|
| 309 | }
|
| 310 |
|
| 311 | /**
|
| 312 | * Register an listener for item delete events. This listener
|
| 313 | * gets called whenever an item is deleted from the node.
|
| 314 | *
|
| 315 | * @param listener The handler for the event
|
| 316 | */
|
| 317 | public void addItemDeleteListener(ItemDeleteListener listener)
|
| 318 | {
|
| 319 | PacketListener delListener = new ItemDeleteTranslator(listener);
|
| 320 | itemDeleteToListenerMap.put(listener, delListener);
|
| 321 | EventContentFilter deleteItem = new EventContentFilter(EventElementType.items.toString(), "retract");
|
| 322 | EventContentFilter purge = new EventContentFilter(EventElementType.purge.toString());
|
| 323 |
|
| 324 | con.addPacketListener(delListener, new OrFilter(deleteItem, purge));
|
| 325 | }
|
| 326 |
|
| 327 | /**
|
| 328 | * Unregister a listener for item delete events.
|
| 329 | *
|
| 330 | * @param listener The handler to unregister
|
| 331 | */
|
| 332 | public void removeItemDeleteListener(ItemDeleteListener listener)
|
| 333 | {
|
| 334 | PacketListener conListener = itemDeleteToListenerMap .remove(listener);
|
| 335 |
|
| 336 | if (conListener != null)
|
| 337 | con.removePacketListener(conListener);
|
| 338 | }
|
| 339 |
|
| 340 | @Override
|
| 341 | public String toString()
|
| 342 | {
|
| 343 | return super.toString() + " " + getClass().getName() + " id: " + id;
|
| 344 | }
|
| 345 |
|
| 346 | protected PubSub createPubsubPacket(Type type, PacketExtension ext)
|
| 347 | {
|
| 348 | return createPubsubPacket(type, ext, null);
|
| 349 | }
|
| 350 |
|
| 351 | protected PubSub createPubsubPacket(Type type, PacketExtension ext, PubSubNamespace ns)
|
| 352 | {
|
| 353 | return PubSubManager.createPubsubPacket(to, type, ext, ns);
|
| 354 | }
|
| 355 |
|
| 356 | protected Packet sendPubsubPacket(Type type, NodeExtension ext)
|
| 357 | throws XMPPException
|
| 358 | {
|
| 359 | return PubSubManager.sendPubsubPacket(con, to, type, ext);
|
| 360 | }
|
| 361 |
|
| 362 | protected Packet sendPubsubPacket(Type type, NodeExtension ext, PubSubNamespace ns)
|
| 363 | throws XMPPException
|
| 364 | {
|
| 365 | return PubSubManager.sendPubsubPacket(con, to, type, ext, ns);
|
| 366 | }
|
| 367 |
|
| 368 |
|
| 369 | private static List<String> getSubscriptionIds(Packet packet)
|
| 370 | {
|
| 371 | HeadersExtension headers = (HeadersExtension)packet.getExtension("headers", "http://jabber.org/protocol/shim");
|
| 372 | List<String> values = null;
|
| 373 |
|
| 374 | if (headers != null)
|
| 375 | {
|
| 376 | values = new ArrayList<String>(headers.getHeaders().size());
|
| 377 |
|
| 378 | for (Header header : headers.getHeaders())
|
| 379 | {
|
| 380 | values.add(header.getValue());
|
| 381 | }
|
| 382 | }
|
| 383 | return values;
|
| 384 | }
|
| 385 |
|
| 386 | /**
|
| 387 | * This class translates low level item publication events into api level objects for
|
| 388 | * user consumption.
|
| 389 | *
|
| 390 | * @author Robin Collier
|
| 391 | */
|
| 392 | public class ItemEventTranslator implements PacketListener
|
| 393 | {
|
| 394 | private ItemEventListener listener;
|
| 395 |
|
| 396 | public ItemEventTranslator(ItemEventListener eventListener)
|
| 397 | {
|
| 398 | listener = eventListener;
|
| 399 | }
|
| 400 |
|
| 401 | public void processPacket(Packet packet)
|
| 402 | {
|
| 403 | EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
|
| 404 | ItemsExtension itemsElem = (ItemsExtension)event.getEvent();
|
| 405 | DelayInformation delay = (DelayInformation)packet.getExtension("delay", "urn:xmpp:delay");
|
| 406 |
|
| 407 | // If there was no delay based on XEP-0203, then try XEP-0091 for backward compatibility
|
| 408 | if (delay == null)
|
| 409 | {
|
| 410 | delay = (DelayInformation)packet.getExtension("x", "jabber:x:delay");
|
| 411 | }
|
| 412 | ItemPublishEvent eventItems = new ItemPublishEvent(itemsElem.getNode(), (List<Item>)itemsElem.getItems(), getSubscriptionIds(packet), (delay == null ? null : delay.getStamp()));
|
| 413 | listener.handlePublishedItems(eventItems);
|
| 414 | }
|
| 415 | }
|
| 416 |
|
| 417 | /**
|
| 418 | * This class translates low level item deletion events into api level objects for
|
| 419 | * user consumption.
|
| 420 | *
|
| 421 | * @author Robin Collier
|
| 422 | */
|
| 423 | public class ItemDeleteTranslator implements PacketListener
|
| 424 | {
|
| 425 | private ItemDeleteListener listener;
|
| 426 |
|
| 427 | public ItemDeleteTranslator(ItemDeleteListener eventListener)
|
| 428 | {
|
| 429 | listener = eventListener;
|
| 430 | }
|
| 431 |
|
| 432 | public void processPacket(Packet packet)
|
| 433 | {
|
| 434 | EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
|
| 435 |
|
| 436 | List<PacketExtension> extList = event.getExtensions();
|
| 437 |
|
| 438 | if (extList.get(0).getElementName().equals(PubSubElementType.PURGE_EVENT.getElementName()))
|
| 439 | {
|
| 440 | listener.handlePurge();
|
| 441 | }
|
| 442 | else
|
| 443 | {
|
| 444 | ItemsExtension itemsElem = (ItemsExtension)event.getEvent();
|
| 445 | Collection<? extends PacketExtension> pubItems = itemsElem.getItems();
|
| 446 | Iterator<RetractItem> it = (Iterator<RetractItem>)pubItems.iterator();
|
| 447 | List<String> items = new ArrayList<String>(pubItems.size());
|
| 448 |
|
| 449 | while (it.hasNext())
|
| 450 | {
|
| 451 | RetractItem item = it.next();
|
| 452 | items.add(item.getId());
|
| 453 | }
|
| 454 |
|
| 455 | ItemDeleteEvent eventItems = new ItemDeleteEvent(itemsElem.getNode(), items, getSubscriptionIds(packet));
|
| 456 | listener.handleDeletedItems(eventItems);
|
| 457 | }
|
| 458 | }
|
| 459 | }
|
| 460 |
|
| 461 | /**
|
| 462 | * This class translates low level node configuration events into api level objects for
|
| 463 | * user consumption.
|
| 464 | *
|
| 465 | * @author Robin Collier
|
| 466 | */
|
| 467 | public class NodeConfigTranslator implements PacketListener
|
| 468 | {
|
| 469 | private NodeConfigListener listener;
|
| 470 |
|
| 471 | public NodeConfigTranslator(NodeConfigListener eventListener)
|
| 472 | {
|
| 473 | listener = eventListener;
|
| 474 | }
|
| 475 |
|
| 476 | public void processPacket(Packet packet)
|
| 477 | {
|
| 478 | EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
|
| 479 | ConfigurationEvent config = (ConfigurationEvent)event.getEvent();
|
| 480 |
|
| 481 | listener.handleNodeConfiguration(config);
|
| 482 | }
|
| 483 | }
|
| 484 |
|
| 485 | /**
|
| 486 | * Filter for {@link PacketListener} to filter out events not specific to the
|
| 487 | * event type expected for this node.
|
| 488 | *
|
| 489 | * @author Robin Collier
|
| 490 | */
|
| 491 | class EventContentFilter implements PacketFilter
|
| 492 | {
|
| 493 | private String firstElement;
|
| 494 | private String secondElement;
|
| 495 |
|
| 496 | EventContentFilter(String elementName)
|
| 497 | {
|
| 498 | firstElement = elementName;
|
| 499 | }
|
| 500 |
|
| 501 | EventContentFilter(String firstLevelEelement, String secondLevelElement)
|
| 502 | {
|
| 503 | firstElement = firstLevelEelement;
|
| 504 | secondElement = secondLevelElement;
|
| 505 | }
|
| 506 |
|
| 507 | public boolean accept(Packet packet)
|
| 508 | {
|
| 509 | if (!(packet instanceof Message))
|
| 510 | return false;
|
| 511 |
|
| 512 | EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
|
| 513 |
|
| 514 | if (event == null)
|
| 515 | return false;
|
| 516 |
|
| 517 | NodeExtension embedEvent = event.getEvent();
|
| 518 |
|
| 519 | if (embedEvent == null)
|
| 520 | return false;
|
| 521 |
|
| 522 | if (embedEvent.getElementName().equals(firstElement))
|
| 523 | {
|
| 524 | if (!embedEvent.getNode().equals(getId()))
|
| 525 | return false;
|
| 526 |
|
| 527 | if (secondElement == null)
|
| 528 | return true;
|
| 529 |
|
| 530 | if (embedEvent instanceof EmbeddedPacketExtension)
|
| 531 | {
|
| 532 | List<PacketExtension> secondLevelList = ((EmbeddedPacketExtension)embedEvent).getExtensions();
|
| 533 |
|
| 534 | if (secondLevelList.size() > 0 && secondLevelList.get(0).getElementName().equals(secondElement))
|
| 535 | return true;
|
| 536 | }
|
| 537 | }
|
| 538 | return false;
|
| 539 | }
|
| 540 | }
|
| 541 | }
|