View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.support.contentdirectory.ui;
17  
18  import org.fourthline.cling.model.action.ActionException;
19  import org.fourthline.cling.model.action.ActionInvocation;
20  import org.fourthline.cling.model.message.UpnpResponse;
21  import org.fourthline.cling.model.meta.Service;
22  import org.fourthline.cling.support.model.BrowseFlag;
23  import org.fourthline.cling.support.model.DIDLContent;
24  import org.fourthline.cling.support.model.SortCriterion;
25  import org.fourthline.cling.support.contentdirectory.callback.Browse;
26  import org.fourthline.cling.model.types.ErrorCode;
27  import org.fourthline.cling.support.model.container.Container;
28  import org.fourthline.cling.support.model.item.Item;
29  
30  import java.util.logging.Logger;
31  
32  import javax.swing.tree.MutableTreeNode;
33  import javax.swing.tree.DefaultTreeModel;
34  import javax.swing.tree.DefaultMutableTreeNode;
35  import javax.swing.SwingUtilities;
36  import java.util.ArrayList;
37  import java.util.List;
38  
39  /**
40   * Updates a tree model after querying a backend <em>ContentDirectory</em> service.
41   *
42   * @author Christian Bauer
43   */
44  public abstract class ContentBrowseActionCallback extends Browse {
45  
46      private static Logger log = Logger.getLogger(ContentBrowseActionCallback.class.getName());
47  
48      final protected DefaultTreeModel treeModel;
49      final protected DefaultMutableTreeNode treeNode;
50  
51      public ContentBrowseActionCallback(Service service, DefaultTreeModel treeModel, DefaultMutableTreeNode treeNode) {
52          super(service, ((Container) treeNode.getUserObject()).getId(), BrowseFlag.DIRECT_CHILDREN, "*", 0, null, new SortCriterion(true, "dc:title"));
53          this.treeModel = treeModel;
54          this.treeNode = treeNode;
55      }
56  
57      public ContentBrowseActionCallback(Service service, DefaultTreeModel treeModel, DefaultMutableTreeNode treeNode,
58                                         String filter, long firstResult, long maxResults, SortCriterion... orderBy) {
59          super(service, ((Container) treeNode.getUserObject()).getId(), BrowseFlag.DIRECT_CHILDREN, filter, firstResult, maxResults, orderBy);
60          this.treeModel = treeModel;
61          this.treeNode = treeNode;
62      }
63  
64      public DefaultTreeModel getTreeModel() {
65          return treeModel;
66      }
67  
68      public DefaultMutableTreeNode getTreeNode() {
69          return treeNode;
70      }
71  
72      public void received(final ActionInvocation actionInvocation, DIDLContent didl) {
73          log.fine("Received browse action DIDL descriptor, creating tree nodes");
74          final List<DefaultMutableTreeNode> childNodes = new ArrayList<>();
75  
76          try {
77  
78              // Containers first
79              for (Container childContainer : didl.getContainers()) {
80                  DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(childContainer) {
81                      @Override
82                      public boolean isLeaf() {
83                          return false;
84                          /* TODO: UPNP VIOLATION: We can't trust the childcount attribute at all, some
85                             servers return 0 even if there are children.
86  
87                          // The 'childCount' is optional, so we always have to assume that unless
88                          // there is a non-zero child count, there are children and we don't know
89                          // anything about them
90                          Container container = ((Container) getUserObject());
91                          Integer childCount = container.getChildCount();
92                          return childCount != null && childCount <= 0;
93                          */
94                      }
95                  };
96                  childNodes.add(childNode);
97              }
98  
99              // Now items
100             for (Item childItem : didl.getItems()) {
101                 DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(childItem) {
102                     @Override
103                     public boolean isLeaf() {
104                         return true;
105                     }
106                 };
107                 childNodes.add(childNode);
108             }
109 
110         } catch (Exception ex) {
111             log.fine("Creating DIDL tree nodes failed: " + ex);
112             actionInvocation.setFailure(
113                     new ActionException(ErrorCode.ACTION_FAILED, "Can't create tree child nodes: " + ex, ex)
114             );
115             failure(actionInvocation, null);
116         }
117 
118         SwingUtilities.invokeLater(new Runnable() {
119             public void run() {
120                 updateTreeModel(childNodes);
121             }
122         });
123     }
124 
125     public void updateStatus(final Status status) {
126         SwingUtilities.invokeLater(new Runnable() {
127             public void run() {
128                 updateStatusUI(status, treeNode, treeModel);
129             }
130         });
131     }
132 
133     @Override
134     public void failure(ActionInvocation invocation, UpnpResponse operation, final String defaultMsg) {
135         SwingUtilities.invokeLater(new Runnable() {
136             public void run() {
137                 failureUI(defaultMsg);
138             }
139         });
140     }
141 
142     protected void updateTreeModel(final List<DefaultMutableTreeNode> childNodes) {
143         log.fine("Adding nodes to tree: " + childNodes.size());
144         // Remove all "old" children such as the loading/progress messages
145         removeChildren();
146 
147         // Insert new children
148         for (DefaultMutableTreeNode childNode : childNodes) {
149             insertChild(childNode);
150         }
151     }
152 
153     protected void removeChildren() {
154         treeNode.removeAllChildren();
155         treeModel.nodeStructureChanged(treeNode);
156     }
157 
158     protected void insertChild(MutableTreeNode childNode) {
159         int index = treeNode.getChildCount() <= 0 ? 0 : treeNode.getChildCount();
160         treeModel.insertNodeInto(childNode, treeNode, index);
161     }
162 
163     public abstract void updateStatusUI(Status status, DefaultMutableTreeNode treeNode, DefaultTreeModel treeModel);
164 
165     public abstract void failureUI(String failureMessage);
166 }