PageRenderTime 42ms CodeModel.GetById 8ms RepoModel.GetById 1ms app.codeStats 0ms

/servers/sip-presence/presence/server/publication-sbb/src/main/java/org/mobicents/slee/sippresence/server/publication/PresenceCompositionPolicy.java

http://mobicents.googlecode.com/
Java | 1010 lines | 754 code | 94 blank | 162 comment | 284 complexity | d7dfcf951bb154664d2749fcf6a21f3a MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.mobicents.slee.sippresence.server.publication;
  23. import java.math.BigDecimal;
  24. import java.util.ArrayList;
  25. import java.util.Collections;
  26. import java.util.Iterator;
  27. import java.util.List;
  28. import java.util.Random;
  29. import javax.xml.datatype.DatatypeFactory;
  30. import javax.xml.datatype.XMLGregorianCalendar;
  31. import javax.xml.parsers.ParserConfigurationException;
  32. import org.mobicents.slee.sipevent.server.publication.StateComposer;
  33. import org.mobicents.xdm.common.util.dom.DomUtils;
  34. import org.w3c.dom.Document;
  35. import org.w3c.dom.Element;
  36. import org.w3c.dom.Node;
  37. import org.w3c.dom.NodeList;
  38. public class PresenceCompositionPolicy implements StateComposer {
  39. private static final Random RANDOM = new Random();
  40. private static String generateNCName() {
  41. // note: any hex string is a valid NCName if does not starts with a number
  42. return new StringBuilder("a").append(Integer.toHexString(RANDOM.nextInt())).toString();
  43. }
  44. /* (non-Javadoc)
  45. * @see org.mobicents.slee.sipevent.server.publication.StateComposer#compose(java.lang.Object, java.lang.Object)
  46. */
  47. @Override
  48. public Document compose(Document document, Document otherDocument) {
  49. if (document == null) {
  50. return otherDocument;
  51. }
  52. if (otherDocument == null) {
  53. return document;
  54. }
  55. final PresenceElementData presenceData = new PresenceElementData(document.getDocumentElement());
  56. final PresenceElementData otherPresenceData = new PresenceElementData(otherDocument.getDocumentElement());
  57. Document newDocument = null;
  58. try {
  59. newDocument = DomUtils.DOCUMENT_BUILDER_NS_AWARE_FACTORY.newDocumentBuilder().newDocument();
  60. } catch (ParserConfigurationException e) {
  61. e.printStackTrace();
  62. return null;
  63. }
  64. Element newPresence = newDocument.createElementNS("urn:ietf:params:xml:ns:pidf", "presence");
  65. newDocument.appendChild(newPresence);
  66. newPresence.setAttribute("entity", presenceData.entity);
  67. for (Node n : composeTuples(presenceData.tuples, otherPresenceData.tuples, newDocument)) {
  68. newPresence.appendChild(n);
  69. }
  70. for (Node n : composeDevices(presenceData.devices, otherPresenceData.devices, newDocument)) {
  71. newPresence.appendChild(n);
  72. }
  73. for (Node n : composePersons(presenceData.persons, otherPresenceData.persons, newDocument)) {
  74. newPresence.appendChild(n);
  75. }
  76. for (Node n : composeAny(presenceData.any, otherPresenceData.any, true,false,false, newDocument)) {
  77. newPresence.appendChild(n);
  78. }
  79. for (Node n : composeNotes(presenceData.notes, otherPresenceData.notes, newDocument)) {
  80. newPresence.appendChild(n);
  81. }
  82. return newDocument;
  83. }
  84. private List<Element> composeTuples(List<Element> tuples, List<Element> otherTuples, Document newDocument) {
  85. List<Element> result = new ArrayList<Element>();
  86. // process all from tuples trying to match each with other tuples
  87. for (Iterator<Element> tuplesIt = tuples.iterator(); tuplesIt.hasNext(); ) {
  88. Element tuple = tuplesIt.next();
  89. for (Iterator<Element> otherTuplesIt = otherTuples.iterator(); otherTuplesIt.hasNext(); ) {
  90. Element otherTuple = otherTuplesIt.next();
  91. Element compositionTuple = composeTuple(tuple, otherTuple, newDocument);
  92. if (compositionTuple != null) {
  93. // the composition has a result
  94. result.add(compositionTuple);
  95. // remove the tuples to not be iterated again
  96. tuplesIt.remove();
  97. otherTuplesIt.remove();
  98. break;
  99. }
  100. }
  101. }
  102. // now add the ones left but replacing the ids
  103. Element e = null;
  104. for (Element tuple : tuples) {
  105. e = (Element) newDocument.importNode(tuple, true);
  106. e.setAttribute("id", generateNCName());
  107. result.add(e);
  108. }
  109. for (Element tuple : otherTuples) {
  110. e = (Element) newDocument.importNode(tuple, true);
  111. e.setAttribute("id", generateNCName());
  112. result.add(e);
  113. }
  114. return result;
  115. }
  116. private Element composeTuple(Element tuple, Element otherTuple, Document newDocument) {
  117. Element result = newDocument.createElement("tuple");
  118. final TupleElementData tupleElementData = new TupleElementData(tuple);
  119. final TupleElementData otherTupleElementData = new TupleElementData(otherTuple);
  120. /*
  121. *
  122. * Service elements (defined in section 10.1.2) If the following
  123. * conditions all apply:
  124. *
  125. * a. If one <tuple> element includes a <contact> element, as
  126. * defined in [RFC3863], other <tuple> elements include an
  127. * identical <contact> element; and
  128. */
  129. if (tupleElementData.contact == null) {
  130. if (otherTupleElementData.contact != null) {
  131. // different contacts
  132. return null;
  133. }
  134. // else ignore no contacts
  135. }
  136. else {
  137. if (otherTupleElementData.contact == null) {
  138. // different contacts
  139. return null;
  140. }
  141. else {
  142. Element composedContact = this.composeContact(tupleElementData.contact, otherTupleElementData.contact,newDocument);
  143. if (composedContact != null) {
  144. result.appendChild(composedContact);
  145. }
  146. else {
  147. return null;
  148. }
  149. }
  150. }
  151. /* b. If one <tuple> element includes a <service-description>
  152. * element, as defined in section 10.5.1, other <tuple> elements
  153. * include an identical <service-description> element. Two
  154. * <service-description> elements are identical if they contain
  155. * identical <service-id> and <version> elements; and
  156. */
  157. if (tupleElementData.serviceDescription == null) {
  158. if (otherTupleElementData.serviceDescription != null) {
  159. // different serviceDescription
  160. return null;
  161. }
  162. // else ignore no serviceDescription
  163. }
  164. else {
  165. if (otherTupleElementData.serviceDescription == null) {
  166. // different serviceDescription
  167. return null;
  168. }
  169. else {
  170. Node composedServiceDescription = this.composeServiceDescription(tupleElementData.serviceDescription, otherTupleElementData.serviceDescription,newDocument);
  171. if (composedServiceDescription != null) {
  172. result.appendChild(composedServiceDescription);
  173. }
  174. else {
  175. return null;
  176. }
  177. }
  178. }
  179. /* c. If one <tuple> element includes a <class> element, as
  180. * defined in section 10.5.1, other <tuple> elements include an
  181. * identical <class> element; and
  182. *
  183. * d. If there are no conflicting elements (i.e. same elements
  184. * with different values) or attributes under the <tuple>
  185. * elements. Different <timestamp> values are not considered as
  186. * a conflict.
  187. *
  188. * then the PS SHALL:
  189. *
  190. * a. Aggregate elements within a <tuple> element that are
  191. * published from different Presence Sources into one <tuple>
  192. * element. Identical elements with the same value and
  193. * attributes SHALL not be duplicated; and
  194. *
  195. * b. Set the <priority> attribute of the <contact> element in
  196. * the aggregated <tuple> element to the highest one among those
  197. * in the input <tuple> elements, if any <priority> attribute is
  198. * present; and
  199. *
  200. * c. Set the <timestamp> of the aggregated <tuple> to the most
  201. * recent one among the ones that contribute to the aggregation;
  202. * and
  203. *
  204. * d. Keep no more than one <description> element from the
  205. * <service-description> elements of the aggregated <tuple>
  206. * element when there are different values of the <description>
  207. * elements.
  208. */
  209. // process status
  210. if (tupleElementData.status == null) {
  211. if (otherTupleElementData.status != null) {
  212. // different status
  213. return null;
  214. }
  215. // else ignore no status
  216. }
  217. else {
  218. if (otherTupleElementData.status == null) {
  219. // different status
  220. return null;
  221. }
  222. else {
  223. Element status = this.composeStatus(tupleElementData.status, otherTupleElementData.status, newDocument);
  224. if (status != null) {
  225. result.appendChild(status);
  226. }
  227. else {
  228. return null;
  229. }
  230. }
  231. }
  232. // process anys
  233. List<Node> anys = composeAny(tupleElementData.any, otherTupleElementData.any, false,false,false, newDocument);
  234. if (anys != null) {
  235. for (Node any : anys) {
  236. result.appendChild(any);
  237. }
  238. }
  239. else {
  240. return null;
  241. }
  242. // process notes
  243. List<Node> notes = composeNotes(tupleElementData.notes, otherTupleElementData.notes, newDocument);
  244. if (notes != null) {
  245. for (Node note : notes) {
  246. result.appendChild(note);
  247. }
  248. }
  249. // process timestamp
  250. if (tupleElementData.timestamp == null) {
  251. if (otherTupleElementData.timestamp != null) {
  252. result.appendChild(newDocument.importNode(otherTupleElementData.timestamp, true));
  253. }
  254. }
  255. else {
  256. if (otherTupleElementData.timestamp == null) {
  257. result.appendChild(newDocument.importNode(tupleElementData.timestamp, true));
  258. }
  259. else {
  260. XMLGregorianCalendar timestamp = null;
  261. XMLGregorianCalendar otherTimestamp = null;
  262. try {
  263. timestamp = DatatypeFactory.newInstance().newXMLGregorianCalendar(tupleElementData.timestamp.getTextContent());
  264. otherTimestamp = DatatypeFactory.newInstance().newXMLGregorianCalendar(otherTupleElementData.timestamp.getTextContent());
  265. } catch (Exception e) {
  266. e.printStackTrace();
  267. }
  268. // need to compare
  269. if (timestamp.compare(otherTimestamp) > 0) {
  270. result.appendChild(newDocument.importNode(tupleElementData.timestamp, true));
  271. }
  272. else {
  273. result.appendChild(newDocument.importNode(otherTupleElementData.timestamp, true));
  274. }
  275. }
  276. }
  277. result.setAttribute("id", generateNCName());
  278. return result;
  279. }
  280. private Node composeServiceDescription(Element serviceDescription,
  281. Element otherServiceDescription, Document newDocument) {
  282. String elementServiceId = null;
  283. String elementVersion = null;
  284. NodeList nodeList = serviceDescription.getChildNodes();
  285. Node node = null;
  286. for (int i=0; i<nodeList.getLength();i++) {
  287. node = nodeList.item(i);
  288. if (DomUtils.isElementNamed(node, "service-id")) {
  289. elementServiceId = ((Element)node).getTextContent();
  290. }
  291. else if (DomUtils.isElementNamed(node, "version")) {
  292. elementVersion = ((Element)node).getTextContent();
  293. }
  294. }
  295. String otherElementServiceId = null;
  296. String otherElementVersion = null;
  297. nodeList = otherServiceDescription.getChildNodes();
  298. for (int i=0; i<nodeList.getLength();i++) {
  299. node = nodeList.item(i);
  300. if (DomUtils.isElementNamed(node, "service-id")) {
  301. otherElementServiceId = ((Element)node).getTextContent();
  302. }
  303. else if (DomUtils.isElementNamed(node, "version")) {
  304. otherElementVersion = ((Element)node).getTextContent();
  305. }
  306. }
  307. if (elementServiceId == null) {
  308. if (otherElementServiceId != null)
  309. return null;
  310. } else if (!elementServiceId.equals(otherElementServiceId))
  311. return null;
  312. if (elementVersion == null) {
  313. if (otherElementVersion != null)
  314. return null;
  315. } else if (!elementVersion.equals(otherElementVersion))
  316. return null;
  317. // service id and version matches, use one of the nodes
  318. return newDocument.importNode(serviceDescription, true);
  319. }
  320. /**
  321. * Compose a contact element from two non null ones.
  322. * @param contact
  323. * @param otherContact
  324. * @return
  325. */
  326. private Element composeContact(Element contact, Element otherContact, Document newDocument) {
  327. Element result = newDocument.createElement("contact");
  328. // compare values
  329. if (contact.getTextContent().equals(otherContact.getTextContent())) {
  330. result.setTextContent(contact.getTextContent());
  331. }
  332. else {
  333. return null;
  334. }
  335. // process priority
  336. BigDecimal contactPriority = null;
  337. String s = contact.getAttribute("priority");
  338. if (s != null) {
  339. s = s.trim();
  340. if (!s.equals("")) {
  341. contactPriority = new BigDecimal(s);
  342. }
  343. }
  344. BigDecimal otherContactPriority = null;
  345. s = otherContact.getAttribute("priority");
  346. if (s != null) {
  347. s = s.trim();
  348. if (!s.equals("")) {
  349. otherContactPriority = new BigDecimal(s);
  350. }
  351. }
  352. if (contactPriority != null) {
  353. if (otherContactPriority == null || otherContactPriority.compareTo(contactPriority) < 0) {
  354. result.appendChild(newDocument.importNode(contact, true));
  355. }
  356. else {
  357. result.appendChild(newDocument.importNode(otherContact, true));
  358. }
  359. }
  360. else {
  361. if (otherContactPriority != null) {
  362. result.appendChild(newDocument.importNode(otherContact, true));
  363. }
  364. }
  365. return result;
  366. }
  367. private Element composeStatus(Element status, Element otherStatus, Document newDocument) {
  368. Element result = newDocument.createElement("status");
  369. final StatusElementData statusElementData = new StatusElementData(status);
  370. final StatusElementData otherStatusElementData = new StatusElementData(otherStatus);
  371. // check basic
  372. if (statusElementData.basic != null) {
  373. if (otherStatusElementData.basic != null) {
  374. if (statusElementData.basic.getTextContent().equals(otherStatusElementData.basic.getTextContent())) {
  375. result.appendChild(newDocument.importNode(statusElementData.basic,true));
  376. }
  377. else {
  378. // no match
  379. return null;
  380. }
  381. }
  382. else {
  383. // no match
  384. return null;
  385. }
  386. }
  387. else {
  388. if (otherStatusElementData.basic != null) {
  389. // no match
  390. return null;
  391. }
  392. else {
  393. // ignore
  394. }
  395. }
  396. // lets process the anys
  397. List<Node> anys = composeAny(statusElementData.any, otherStatusElementData.any, false,false,false, newDocument);
  398. if (anys != null) {
  399. if (result == null) {
  400. result = newDocument.createElement("status");
  401. }
  402. for (Node any : anys) {
  403. result.appendChild(any);
  404. }
  405. }
  406. else {
  407. return null;
  408. }
  409. return result;
  410. }
  411. private List<Node> composeAny(List<Element> anys, List<Element> otherAnys, boolean allowConflicts, boolean keepRecentInConflict, boolean anysIsOlder, Document newDocument) {
  412. ArrayList<Node> result = new ArrayList<Node>();
  413. for (Iterator<Element> anysIt = anys.iterator(); anysIt.hasNext(); ) {
  414. Element anysElement = anysIt.next();
  415. for (Iterator<Element> otherAnysIt = otherAnys.iterator(); otherAnysIt.hasNext(); ) {
  416. Element otherAnysElement = otherAnysIt.next();
  417. if (DomUtils.getElementName(anysElement).equals(DomUtils.getElementName(otherAnysElement))) {
  418. // same element name
  419. if (anysElement.getNamespaceURI() != null) {
  420. if (!anysElement.getNamespaceURI().equals(otherAnysElement.getNamespaceURI())) {
  421. continue;
  422. }
  423. }
  424. else {
  425. if (otherAnysElement.getNamespaceURI() != null) {
  426. continue;
  427. }
  428. }
  429. // same namespace
  430. // check for conflict
  431. if (isConflict(anysElement, otherAnysElement)) {
  432. if (allowConflicts) {
  433. if (keepRecentInConflict) {
  434. if (anysIsOlder) {
  435. result.add(newDocument.importNode(otherAnysElement,true));
  436. }
  437. else {
  438. result.add(newDocument.importNode(anysElement,true));
  439. }
  440. anysIt.remove();
  441. otherAnysIt.remove();
  442. break;
  443. }
  444. }
  445. else {
  446. return null;
  447. }
  448. }
  449. else {
  450. if (anysIsOlder) {
  451. result.add(newDocument.importNode(otherAnysElement,true));
  452. }
  453. else {
  454. result.add(newDocument.importNode(anysElement,true));
  455. }
  456. anysIt.remove();
  457. otherAnysIt.remove();
  458. break;
  459. }
  460. }
  461. }
  462. }
  463. // now add the ones left
  464. for (Element e : anys) {
  465. result.add(newDocument.importNode(e,true));
  466. }
  467. for (Element e : otherAnys) {
  468. result.add(newDocument.importNode(e,true));
  469. }
  470. return result;
  471. }
  472. private boolean isConflict(Element element, Element otherElement) {
  473. return !element.isEqualNode(otherElement);
  474. }
  475. private List<Node> composeNotes(List<Element> notes,List<Element> otherNotes, Document newDocument) {
  476. ArrayList<Node> result = new ArrayList<Node>();
  477. // process all from notes trying to match each with otherNote
  478. for (Iterator<Element> notesIt = notes.iterator(); notesIt.hasNext(); ) {
  479. Element note = notesIt.next();
  480. for (Iterator<Element> otherNotesIt = otherNotes.iterator(); otherNotesIt.hasNext(); ) {
  481. Element otherNote = otherNotesIt.next();
  482. if (note.isEqualNode(otherNote)) {
  483. result.add(newDocument.importNode(note, true));
  484. notesIt.remove();
  485. otherNotesIt.remove();
  486. break;
  487. }
  488. }
  489. }
  490. // now add the ones left
  491. for (Element note : notes) {
  492. result.add(newDocument.importNode(note, true));
  493. }
  494. for (Element note : otherNotes) {
  495. result.add(newDocument.importNode(note, true));
  496. }
  497. return result;
  498. }
  499. private List<Element> composeDevices(
  500. List<Element> devices,
  501. List<Element> otherDevices, Document newDocument) {
  502. ArrayList<Element> result = new ArrayList<Element>();
  503. for (Iterator<Element> it = devices.iterator(); it.hasNext(); ) {
  504. Element device = it.next();
  505. for (Iterator<Element> otherIt = otherDevices.iterator(); otherIt.hasNext(); ) {
  506. Element otherDevice = otherIt.next();
  507. Element compositionDevice = composeDevice(device, otherDevice, newDocument);
  508. if (compositionDevice != null) {
  509. // the composition has a result
  510. result.add(compositionDevice);
  511. // remove both to not be iterated again
  512. it.remove();
  513. otherIt.remove();
  514. break;
  515. }
  516. }
  517. }
  518. // now add the ones left but replacing the ids
  519. Element e = null;
  520. for (Element device : devices) {
  521. e = (Element) newDocument.importNode(device, true);
  522. e.setAttribute("id",generateNCName());
  523. result.add(e);
  524. }
  525. for (Element device : otherDevices) {
  526. e = (Element) newDocument.importNode(device, true);
  527. e.setAttribute("id",generateNCName());
  528. result.add(e);
  529. }
  530. return result;
  531. }
  532. private Element composeDevice(Element device,
  533. Element otherDevice, Document newDocument) {
  534. /*
  535. * If the <deviceID> of the <device> elements that are published from
  536. * different Presence Sources match
  537. *
  538. * then the PS SHALL
  539. *
  540. * a. Aggregate the non-conflicting elements within one <device>
  541. * element; and
  542. *
  543. * b. Set the <timestamp> of the aggregated <device> element to the most
  544. * recent one among the ones that contribute to the aggregation; and
  545. *
  546. * c. Use the element from the most recent publication for conflicting
  547. * elements.
  548. *
  549. */
  550. final DeviceElementData deviceElementData = new DeviceElementData(device);
  551. final DeviceElementData otherDeviceElementData = new DeviceElementData(otherDevice);
  552. if (deviceElementData.deviceID.getTextContent().equals(otherDeviceElementData.deviceID.getTextContent())) {
  553. Element result = newDocument.createElement("device");
  554. result.appendChild(newDocument.importNode(deviceElementData.deviceID, true));
  555. // process timestamp
  556. boolean deviceIsOlder = false;
  557. if (deviceElementData.timestamp == null) {
  558. if (otherDeviceElementData.timestamp != null) {
  559. result.appendChild(newDocument.importNode(otherDeviceElementData.timestamp, true));
  560. deviceIsOlder = true;
  561. }
  562. }
  563. else {
  564. if (otherDeviceElementData.timestamp == null) {
  565. result.appendChild(newDocument.importNode(deviceElementData.timestamp, true));
  566. }
  567. else {
  568. XMLGregorianCalendar timestamp = null;
  569. XMLGregorianCalendar otherTimestamp = null;
  570. try {
  571. timestamp = DatatypeFactory.newInstance().newXMLGregorianCalendar(deviceElementData.timestamp.getTextContent());
  572. otherTimestamp = DatatypeFactory.newInstance().newXMLGregorianCalendar(otherDeviceElementData.timestamp.getTextContent());
  573. } catch (Exception e) {
  574. e.printStackTrace();
  575. }
  576. // need to compare
  577. if (timestamp.compare(otherTimestamp) > 0) {
  578. result.appendChild(newDocument.importNode(deviceElementData.timestamp, true));
  579. }
  580. else {
  581. result.appendChild(newDocument.importNode(otherDeviceElementData.timestamp, true));
  582. deviceIsOlder = true;
  583. }
  584. }
  585. }
  586. // process anys
  587. List<Node> anys = composeAny(deviceElementData.any, otherDeviceElementData.any, true,true,deviceIsOlder, newDocument);
  588. if (anys != null) {
  589. for (Node any : anys) {
  590. result.appendChild(any);
  591. }
  592. }
  593. // process notes
  594. List<Node> notes = composeNotes(deviceElementData.notes, otherDeviceElementData.notes, newDocument);
  595. if (notes != null) {
  596. for (Node note : notes) {
  597. result.appendChild(note);
  598. }
  599. }
  600. result.setAttribute("id", generateNCName());
  601. return result;
  602. }
  603. else {
  604. return null;
  605. }
  606. }
  607. private List<Element> composePersons(
  608. List<Element> persons,
  609. List<Element> otherPersons, Document newDocument) {
  610. ArrayList<Element> result = new ArrayList<Element>();
  611. for (Iterator<Element> it = persons.iterator(); it.hasNext(); ) {
  612. Element person = it.next();
  613. for (Iterator<Element> otherIt = otherPersons.iterator(); otherIt.hasNext(); ) {
  614. Element otherPerson = otherIt.next();
  615. Element compositionPerson = composePerson(person, otherPerson, newDocument);
  616. if (compositionPerson != null) {
  617. // the composition has a result
  618. result.add(compositionPerson);
  619. // remove both to not be iterated again
  620. it.remove();
  621. otherIt.remove();
  622. break;
  623. }
  624. }
  625. }
  626. // now add the ones left but replacing the ids
  627. Element e = null;
  628. for (Element person : persons) {
  629. e = (Element) newDocument.importNode(person, true);
  630. e.setAttribute("id",generateNCName());
  631. result.add(e);
  632. }
  633. for (Element person : otherPersons) {
  634. e = (Element) newDocument.importNode(person, true);
  635. e.setAttribute("id",generateNCName());
  636. result.add(e);
  637. }
  638. return result;
  639. }
  640. private Element composePerson(Element person,
  641. Element otherPerson, Document newDocument) {
  642. /*
  643. * If the following conditions all apply:
  644. *
  645. * a. If one <person> element includes a <class> element, as defined in
  646. * section 10.5.1, other <person> elements include an identical <class>
  647. * element; and
  648. *
  649. * b. If there are no conflicting elements (same elements with different
  650. * values or attributes) under the <person> elements. Different
  651. * <timestamp> values are not considered as a conflict.
  652. *
  653. * then the PS SHALL:
  654. *
  655. * a. Aggregate elements within a <person> element that are published
  656. * from different Presence Sources into one <person> element. Identical
  657. * elements with the same value SHALL not be duplicated.
  658. *
  659. * b. Set the <timestamp> of the aggregated <person> element to the most
  660. * recent one among the ones that contribute to the aggregation. In any
  661. * other case, the PS SHALL keep <person> elements from different
  662. * Presence Sources separate.
  663. */
  664. Element result = newDocument.createElement("person");
  665. final PersonElementData personElementData = new PersonElementData(person);
  666. final PersonElementData otherPersonElementData = new PersonElementData(otherPerson);
  667. // process anys
  668. List<Node> anys = composeAny(personElementData.any, otherPersonElementData.any, false,false,false, newDocument);
  669. if (anys != null) {
  670. for (Node any : anys) {
  671. result.appendChild(any);
  672. }
  673. }
  674. else {
  675. return null;
  676. }
  677. // process notes
  678. List<Node> notes = composeNotes(personElementData.notes, otherPersonElementData.notes, newDocument);
  679. if (notes != null) {
  680. for (Node note : notes) {
  681. result.appendChild(note);
  682. }
  683. }
  684. // process timestamp
  685. if (personElementData.timestamp == null) {
  686. if (otherPersonElementData.timestamp != null) {
  687. result.appendChild(newDocument.importNode(otherPersonElementData.timestamp, true));
  688. }
  689. }
  690. else {
  691. if (otherPersonElementData.timestamp == null) {
  692. result.appendChild(newDocument.importNode(personElementData.timestamp, true));
  693. }
  694. else {
  695. XMLGregorianCalendar timestamp = null;
  696. XMLGregorianCalendar otherTimestamp = null;
  697. try {
  698. timestamp = DatatypeFactory.newInstance().newXMLGregorianCalendar(personElementData.timestamp.getTextContent());
  699. otherTimestamp = DatatypeFactory.newInstance().newXMLGregorianCalendar(otherPersonElementData.timestamp.getTextContent());
  700. } catch (Exception e) {
  701. e.printStackTrace();
  702. }
  703. // need to compare
  704. if (timestamp.compare(otherTimestamp) > 0) {
  705. result.appendChild(newDocument.importNode(personElementData.timestamp, true));
  706. }
  707. else {
  708. result.appendChild(newDocument.importNode(otherPersonElementData.timestamp, true));
  709. }
  710. }
  711. }
  712. result.setAttribute("id", generateNCName());
  713. return result;
  714. }
  715. // --- temp element data structures
  716. private static final List<Element> EMPTY_LIST = Collections.emptyList();
  717. private static class PresenceElementData {
  718. List<Element> tuples;
  719. List<Element> devices;
  720. List<Element> persons;
  721. List<Element> any;
  722. List<Element> notes;
  723. String entity;
  724. PresenceElementData(Element element) {
  725. entity = element.getAttribute("entity");
  726. NodeList childs = element.getChildNodes();
  727. Node child = null;
  728. for(int i=0;i<childs.getLength();i++) {
  729. child = childs.item(i);
  730. if(DomUtils.isElementNamed(child, "tuple")) {
  731. if(tuples == null) {
  732. tuples = new ArrayList<Element>();
  733. }
  734. tuples.add((Element) child);
  735. }
  736. else if(DomUtils.isElementNamed(child, "device")) {
  737. if(devices == null) {
  738. devices = new ArrayList<Element>();
  739. }
  740. devices.add((Element) child);
  741. }
  742. else if(DomUtils.isElementNamed(child, "person")) {
  743. if(persons == null) {
  744. persons = new ArrayList<Element>();
  745. }
  746. persons.add((Element) child);
  747. }
  748. else if(DomUtils.isElementNamed(child, "note")) {
  749. if(notes == null) {
  750. notes = new ArrayList<Element>();
  751. }
  752. notes.add((Element) child);
  753. }
  754. else if (child.getNodeType() == Node.ELEMENT_NODE) {
  755. if(any == null) {
  756. any = new ArrayList<Element>();
  757. }
  758. any.add((Element) child);
  759. }
  760. }
  761. if (any == null) {
  762. any = EMPTY_LIST;
  763. }
  764. if (devices == null) {
  765. devices = EMPTY_LIST;
  766. }
  767. if (notes == null) {
  768. notes = EMPTY_LIST;
  769. }
  770. if (persons == null) {
  771. persons = EMPTY_LIST;
  772. }
  773. if (tuples == null) {
  774. tuples = EMPTY_LIST;
  775. }
  776. }
  777. }
  778. private static class TupleElementData {
  779. Element serviceDescription;
  780. Element contact;
  781. Element status;
  782. Element timestamp;
  783. List<Element> any;
  784. List<Element> notes;
  785. TupleElementData(Element element) {
  786. NodeList childs = element.getChildNodes();
  787. Node child = null;
  788. for(int i=0;i<childs.getLength();i++) {
  789. child = childs.item(i);
  790. if(DomUtils.isElementNamed(child, "contact")) {
  791. contact = (Element) child;
  792. }
  793. else if(DomUtils.isElementNamed(child, "service-description")) {
  794. serviceDescription = (Element) child;
  795. }
  796. else if(DomUtils.isElementNamed(child, "status")) {
  797. status = (Element) child;
  798. }
  799. else if(DomUtils.isElementNamed(child, "timestamp")) {
  800. timestamp = (Element) child;
  801. }
  802. else if(DomUtils.isElementNamed(child, "note")) {
  803. if(notes == null) {
  804. notes = new ArrayList<Element>();
  805. }
  806. notes.add((Element) child);
  807. }
  808. else if (child.getNodeType() == Node.ELEMENT_NODE) {
  809. if(any == null) {
  810. any = new ArrayList<Element>();
  811. }
  812. any.add((Element) child);
  813. }
  814. }
  815. if (any == null) {
  816. any = EMPTY_LIST;
  817. }
  818. if (notes == null) {
  819. notes = EMPTY_LIST;
  820. }
  821. }
  822. }
  823. private static class StatusElementData {
  824. Element basic;
  825. List<Element> any;
  826. StatusElementData(Element element) {
  827. NodeList childs = element.getChildNodes();
  828. Node child = null;
  829. for(int i=0;i<childs.getLength();i++) {
  830. child = childs.item(i);
  831. if(DomUtils.isElementNamed(child, "basic")) {
  832. basic = (Element) child;
  833. }
  834. else if (child.getNodeType() == Node.ELEMENT_NODE) {
  835. if(any == null) {
  836. any = new ArrayList<Element>();
  837. }
  838. any.add((Element) child);
  839. }
  840. }
  841. if (any == null) {
  842. any = EMPTY_LIST;
  843. }
  844. }
  845. }
  846. private static class DeviceElementData {
  847. Element deviceID;
  848. Element timestamp;
  849. List<Element> any;
  850. List<Element> notes;
  851. DeviceElementData(Element element) {
  852. NodeList childs = element.getChildNodes();
  853. Node child = null;
  854. for(int i=0;i<childs.getLength();i++) {
  855. child = childs.item(i);
  856. if(DomUtils.isElementNamed(child, "deviceID")) {
  857. deviceID = (Element) child;
  858. }
  859. else if(DomUtils.isElementNamed(child, "timestamp")) {
  860. timestamp = (Element) child;
  861. }
  862. else if(DomUtils.isElementNamed(child, "note")) {
  863. if(notes == null) {
  864. notes = new ArrayList<Element>();
  865. }
  866. notes.add((Element) child);
  867. }
  868. else if (child.getNodeType() == Node.ELEMENT_NODE) {
  869. if(any == null) {
  870. any = new ArrayList<Element>();
  871. }
  872. any.add((Element) child);
  873. }
  874. }
  875. if (any == null) {
  876. any = EMPTY_LIST;
  877. }
  878. if (notes == null) {
  879. notes = EMPTY_LIST;
  880. }
  881. }
  882. }
  883. private static class PersonElementData {
  884. Element timestamp;
  885. List<Element> any;
  886. List<Element> notes;
  887. PersonElementData(Element element) {
  888. NodeList childs = element.getChildNodes();
  889. Node child = null;
  890. for(int i=0;i<childs.getLength();i++) {
  891. child = childs.item(i);
  892. if(DomUtils.isElementNamed(child, "timestamp")) {
  893. timestamp = (Element) child;
  894. }
  895. else if(DomUtils.isElementNamed(child, "note")) {
  896. if(notes == null) {
  897. notes = new ArrayList<Element>();
  898. }
  899. notes.add((Element) child);
  900. }
  901. else if (child.getNodeType() == Node.ELEMENT_NODE) {
  902. if(any == null) {
  903. any = new ArrayList<Element>();
  904. }
  905. any.add((Element) child);
  906. }
  907. }
  908. if (any == null) {
  909. any = EMPTY_LIST;
  910. }
  911. if (notes == null) {
  912. notes = EMPTY_LIST;
  913. }
  914. }
  915. }
  916. }