]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.model/src/org/simantics/browsing/ui/model/browsecontexts/BrowseContext.java
Sync git svn branch with SVN repository r33345.
[simantics/platform.git] / bundles / org.simantics.browsing.ui.model / src / org / simantics / browsing / ui / model / browsecontexts / BrowseContext.java
1 /*******************************************************************************\r
2  * Copyright (c) 2010, 2011 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.browsing.ui.model.browsecontexts;\r
13 \r
14 import java.util.ArrayList;\r
15 import java.util.Arrays;\r
16 import java.util.Collection;\r
17 import java.util.Collections;\r
18 import java.util.HashMap;\r
19 import java.util.HashSet;\r
20 import java.util.List;\r
21 import java.util.Map;\r
22 import java.util.Set;\r
23 \r
24 import org.eclipse.jface.resource.ImageDescriptor;\r
25 import org.eclipse.swt.widgets.Display;\r
26 import org.eclipse.swt.widgets.Event;\r
27 import org.eclipse.swt.widgets.Shell;\r
28 import org.simantics.browsing.ui.BuiltinKeys;\r
29 import org.simantics.browsing.ui.CheckedState;\r
30 import org.simantics.browsing.ui.NodeContext;\r
31 import org.simantics.browsing.ui.common.ColumnKeys;\r
32 import org.simantics.browsing.ui.common.NodeContextBuilder;\r
33 import org.simantics.browsing.ui.content.CompositeImageDecorator;\r
34 import org.simantics.browsing.ui.content.CompositeLabelDecorator;\r
35 import org.simantics.browsing.ui.content.ImageDecorator;\r
36 import org.simantics.browsing.ui.content.LabelDecorator;\r
37 import org.simantics.browsing.ui.content.Labeler.Modifier;\r
38 import org.simantics.browsing.ui.model.InvalidContribution;\r
39 import org.simantics.browsing.ui.model.actions.ActionBrowseContext;\r
40 import org.simantics.browsing.ui.model.check.CheckedStateContribution;\r
41 import org.simantics.browsing.ui.model.children.ChildContribution;\r
42 import org.simantics.browsing.ui.model.imagedecorators.ImageDecorationContribution;\r
43 import org.simantics.browsing.ui.model.images.ImageContribution;\r
44 import org.simantics.browsing.ui.model.labeldecorators.LabelDecorationContribution;\r
45 import org.simantics.browsing.ui.model.labels.LabelContribution;\r
46 import org.simantics.browsing.ui.model.modifiers.ModifierContribution;\r
47 import org.simantics.browsing.ui.model.modifiers.NoModifierRule;\r
48 import org.simantics.browsing.ui.model.nodetypes.EntityNodeType;\r
49 import org.simantics.browsing.ui.model.nodetypes.NodeType;\r
50 import org.simantics.browsing.ui.model.nodetypes.NodeTypeMultiMap;\r
51 import org.simantics.browsing.ui.model.nodetypes.OrderedNodeTypeMultiMap;\r
52 import org.simantics.browsing.ui.model.nodetypes.SpecialNodeType;\r
53 import org.simantics.browsing.ui.model.sorters.AlphanumericSorter;\r
54 import org.simantics.browsing.ui.model.sorters.Sorter;\r
55 import org.simantics.browsing.ui.model.sorters.SorterContribution;\r
56 import org.simantics.browsing.ui.model.tooltips.TooltipContribution;\r
57 import org.simantics.browsing.ui.model.visuals.FlatNodeContribution;\r
58 import org.simantics.browsing.ui.model.visuals.VisualsContribution;\r
59 import org.simantics.db.ReadGraph;\r
60 import org.simantics.db.RequestProcessor;\r
61 import org.simantics.db.Resource;\r
62 import org.simantics.db.exception.DatabaseException;\r
63 import org.simantics.db.exception.ResourceNotFoundException;\r
64 import org.simantics.db.layer0.variable.Variable;\r
65 import org.simantics.db.request.Read;\r
66 import org.simantics.graphviz.Graph;\r
67 import org.simantics.graphviz.Node;\r
68 import org.simantics.graphviz.ui.GraphvizComponent;\r
69 import org.simantics.scl.reflection.OntologyVersions;\r
70 import org.simantics.viewpoint.ontology.ViewpointResource;\r
71 \r
72 /**\r
73  * BrowseContext holds all contributions related to given set of browse contexts.\r
74  * \r
75  * @author Hannu Niemistö\r
76  */\r
77 public class BrowseContext {\r
78         \r
79         public static final boolean DEBUG = false;\r
80         \r
81     NodeTypeMultiMap<ChildContribution> childContributions = new NodeTypeMultiMap<ChildContribution>();\r
82     NodeTypeMultiMap<ChildContribution> parentContributions = new NodeTypeMultiMap<ChildContribution>();\r
83     OrderedNodeTypeMultiMap<LabelContribution> labelContributions = new OrderedNodeTypeMultiMap<LabelContribution>();\r
84     OrderedNodeTypeMultiMap<ImageContribution> imageContributions = new OrderedNodeTypeMultiMap<ImageContribution>();\r
85     OrderedNodeTypeMultiMap<CheckedStateContribution> checkedStateContributions = new OrderedNodeTypeMultiMap<CheckedStateContribution>();\r
86     OrderedNodeTypeMultiMap<LabelDecorationContribution> labelDecorationContributions = new OrderedNodeTypeMultiMap<LabelDecorationContribution>();\r
87     OrderedNodeTypeMultiMap<ImageDecorationContribution> imageDecorationContributions = new OrderedNodeTypeMultiMap<ImageDecorationContribution>();\r
88     OrderedNodeTypeMultiMap<ModifierContribution> modifierContributions = new OrderedNodeTypeMultiMap<ModifierContribution>();\r
89     OrderedNodeTypeMultiMap<SorterContribution> sorterContributions = new OrderedNodeTypeMultiMap<SorterContribution>();\r
90     OrderedNodeTypeMultiMap<FlatNodeContribution> flatNodeContributions = new OrderedNodeTypeMultiMap<FlatNodeContribution>();\r
91     OrderedNodeTypeMultiMap<TooltipContribution> tooltipContributions = new OrderedNodeTypeMultiMap<>();\r
92 \r
93     private final String[] uris; \r
94 \r
95     private BrowseContext(String[] uris) {\r
96         if (uris == null)\r
97             throw new NullPointerException("null URIs");\r
98         this.uris = uris;\r
99     }\r
100 \r
101     public String[] getURIs() {\r
102         return uris;\r
103     }\r
104 \r
105     public static BrowseContext get(ReadGraph graph,NodeContext context,BrowseContext defaultContext, boolean useNodeBrowseContexts) throws DatabaseException {\r
106         if(!useNodeBrowseContexts) return defaultContext;\r
107         BrowseContext mbc = graph.syncRequest(new ResolveBrowseContext(context));\r
108         if(mbc != null) return mbc;\r
109         BrowseContext parentContext = (BrowseContext)context.getConstant(BuiltinKeys.BROWSE_CONTEXT);\r
110         if(parentContext != null) return parentContext;\r
111         return defaultContext;\r
112     }\r
113 \r
114     /**\r
115      * Creates a new BrowseContext for the given Collection of {@link Resource}s.\r
116      * \r
117      * @param g\r
118      * @param browseContextResources\r
119      * @return new BrowseContext\r
120      * @throws DatabaseException\r
121      * @throws InvalidContribution\r
122      */\r
123     public static BrowseContext create(ReadGraph g, Collection<Resource> browseContextResources) throws DatabaseException, InvalidContribution {\r
124         ViewpointResource vr = ViewpointResource.getInstance(g);\r
125         BrowseContext browseContext = new BrowseContext( BrowseContexts.toSortedURIs(g, browseContextResources) );\r
126         for(Resource browseContextResource : findSubcontexts(g, browseContextResources)) {\r
127             \r
128             for(Resource childContributionResource : \r
129                 g.getObjects(browseContextResource, vr.BrowseContext_HasChildContribution)) {\r
130                 ChildContribution contribution = ChildContribution.create(g, childContributionResource);\r
131                 browseContext.childContributions.put(contribution.getParentNodeType(), contribution);\r
132                 browseContext.parentContributions.put(contribution.getChildNodeType(), contribution);\r
133             }\r
134             \r
135             for(Resource visualsContributionResource : \r
136                 g.getObjects(browseContextResource, vr.BrowseContext_HasVisualsContribution)) {\r
137                 VisualsContribution.load(g, visualsContributionResource,\r
138                         browseContext.labelContributions,\r
139                         browseContext.imageContributions,\r
140                         browseContext.checkedStateContributions,\r
141                         browseContext.labelDecorationContributions,\r
142                         browseContext.imageDecorationContributions,\r
143                         browseContext.modifierContributions,\r
144                         browseContext.sorterContributions,\r
145                         browseContext.flatNodeContributions,\r
146                         browseContext.tooltipContributions\r
147                         );\r
148             }\r
149         }\r
150         //browseContext.visualize();\r
151         return browseContext;\r
152     }\r
153 \r
154     public static Set<String> getBrowseContextClosure(RequestProcessor processor, final Set<String> browseContexts) throws DatabaseException {\r
155         return processor.syncRequest(new Read<Set<String>>() {\r
156             @Override\r
157             public Set<String> perform(ReadGraph graph) throws DatabaseException {\r
158                 Collection<Resource> browseContextResources = new ArrayList<Resource>(browseContexts.size());\r
159                 for (String browseContext : browseContexts) {\r
160                     try {\r
161                         browseContextResources.add(graph.getResource(browseContext));\r
162                     } catch (ResourceNotFoundException e) {\r
163                         // Expected result, if no modelled contributions exist.\r
164                         //System.err.println("Didn't find " + browseContext + " while loading model browser.");\r
165                     }\r
166                 }\r
167                 Collection<Resource> allBrowseContextResources = BrowseContext.findSubcontexts(graph, browseContextResources);\r
168                 Set<String> result = new HashSet<String>();\r
169                 for (Resource r : allBrowseContextResources)\r
170                     result.add(graph.getURI(r));\r
171                         return result;\r
172             }\r
173         });\r
174     }\r
175 \r
176     public static Collection<Resource> findSubcontexts(ReadGraph g,\r
177             Collection<Resource> browseContexts) throws DatabaseException {\r
178         ViewpointResource vr = ViewpointResource.getInstance(g);\r
179         HashSet<Resource> result = new HashSet<Resource>(browseContexts);\r
180         ArrayList<Resource> stack = new ArrayList<Resource>(browseContexts);\r
181         while(!stack.isEmpty()) {\r
182             Resource cur = stack.remove(stack.size()-1);\r
183             for(Resource sc : g.getObjects(cur, vr.BrowseContext_Includes))\r
184                 if(result.add(sc))\r
185                     stack.add(sc);\r
186         }\r
187         return result;\r
188     }\r
189     \r
190     /**\r
191      * Finds the possible children of the given {@link NodeContext} parameter.\r
192      * \r
193      * @param graph\r
194      * @param parent\r
195      * @return Collection of children or an empty collection in case node has no children\r
196      * @throws DatabaseException\r
197      */\r
198     public Collection<NodeContext> getChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
199         if(isFlattened(graph, parent))\r
200             return Collections.emptyList();\r
201         else\r
202             return getChildrenImpl(graph, parent);\r
203     }\r
204     \r
205     private Collection<NodeContext> getChildrenImpl(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
206         NodeType nodeType = getNodeType(graph, parent);\r
207         if(nodeType == null)\r
208             return Collections.emptyList();\r
209         ArrayList<NodeContext> result = new ArrayList<NodeContext>();\r
210         Collection<ChildContribution> contributions = childContributions.get(graph, nodeType);\r
211         if(contributions.size() > 1) {\r
212             Map<String,ChildContribution> contribs = new HashMap<>(); \r
213             for(ChildContribution contribution : contributions) {\r
214                 String identifier = contribution.getIdentifier();\r
215                 ChildContribution current = contribs.get(identifier);\r
216                 if(current != null && current.getPriority() > contribution.getPriority()) continue;\r
217                 contribs.put(identifier, contribution);\r
218             }\r
219             contributions = contribs.values();\r
220         }\r
221         for(ChildContribution contribution : contributions) {\r
222                 Collection<NodeContext> children = contribution.getChildren(graph, parent);\r
223             result.addAll(children);\r
224             if(DEBUG) {\r
225                     System.err.println("contribution: " + contribution.getIdentifier());\r
226                     for(NodeContext ctx : children)\r
227                         System.err.println("-" + ctx);\r
228             }\r
229         }\r
230         \r
231         // Sorting the result\r
232         if(!result.isEmpty()) {            \r
233             for(SorterContribution contribution : sorterContributions.get(graph, nodeType)) { \r
234                 Sorter sorter = contribution.getSorter(graph, parent);\r
235                 if(sorter != null) {\r
236                     sorter.sort(graph, this, result);\r
237                     return result;\r
238                 }\r
239             }\r
240             AlphanumericSorter.INSTANCE.sort(graph, this, result);\r
241         }\r
242         \r
243         result = flatten(graph, result);\r
244         //result = augment(graph, result);\r
245         \r
246         return result;\r
247     }\r
248     \r
249     private ArrayList<NodeContext> flatten(ReadGraph graph, ArrayList<NodeContext> result) throws DatabaseException {\r
250         ArrayList<NodeContext> flattened = new ArrayList<NodeContext>();\r
251         for(NodeContext node : result)\r
252             if(isFlattened(graph, node)) {\r
253                 flattened.add(node);\r
254                 flattened.addAll(getChildrenImpl(graph, node));\r
255             }\r
256             else\r
257                 flattened.add(node);\r
258         return flattened;\r
259     }\r
260     \r
261     public static ArrayList<NodeContext> augment(ReadGraph graph, BrowseContext bc, Collection<NodeContext> contexts, boolean resolveABC) throws DatabaseException {\r
262         ArrayList<NodeContext> result = new ArrayList<NodeContext>();\r
263         for(NodeContext context : contexts) {\r
264                 ActionBrowseContext abc = null;\r
265                 if(resolveABC) {\r
266                         abc = graph.syncRequest(new ResolveActionBrowseContext(context));\r
267                         if(abc == null) abc = (ActionBrowseContext)context.getConstant(BuiltinKeys.ACTION_BROWSE_CONTEXT);\r
268                 }\r
269             result.add(NodeContextBuilder.buildWithData(NodeType.KEY_SEQUENCE_EXT,\r
270                     new Object[] {\r
271                     context.getConstant(BuiltinKeys.INPUT), \r
272                     context.getConstant(NodeType.TYPE),\r
273                     context.getConstant(BuiltinKeys.UI_CONTEXT),\r
274                     bc, abc}));\r
275         }\r
276         return result;\r
277     }\r
278     \r
279     private boolean isFlattened(ReadGraph graph, NodeContext node) throws DatabaseException {\r
280         NodeType nodeType = getNodeType(graph, node);\r
281         return nodeType != null && !flatNodeContributions.get(graph, nodeType).isEmpty();\r
282     }\r
283 \r
284     /**\r
285      * Finds the possible parents of the given {@link NodeContext} parameter.\r
286      * \r
287      * @param graph\r
288      * @param child\r
289      * @return Collection of parents or an empty Collection in case node has no parents.\r
290      * @throws DatabaseException\r
291      */\r
292     public Collection<NodeContext> getParents(ReadGraph graph, NodeContext child) throws DatabaseException {\r
293         NodeType nodeType = getNodeType(graph, child);\r
294         if(nodeType == null)\r
295             return Collections.emptyList();\r
296         ArrayList<NodeContext> result = new ArrayList<NodeContext>();\r
297         for(ChildContribution contribution : parentContributions.get(graph, nodeType)) {\r
298             result.addAll(contribution.getParents(graph, child));\r
299         }\r
300         return result;\r
301     }\r
302     \r
303     public boolean hasChildren(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
304         NodeType nodeType = getNodeType(graph, parent);\r
305         if(nodeType == null)\r
306             return false;        \r
307         for(ChildContribution contribution : childContributions.get(graph, nodeType))\r
308             if(contribution.hasChildren(graph, parent))\r
309                 return true;\r
310         return false;\r
311     }\r
312     \r
313     private static NodeType getNodeType(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
314         NodeType nodeType = parent.getConstant(NodeType.TYPE);\r
315         if(nodeType == null) {            \r
316             // TODO remove this code when root of model browser is fixed\r
317             Object input = parent.getConstant(BuiltinKeys.INPUT);\r
318             if(input instanceof Resource) {\r
319                 nodeType = EntityNodeType.getNodeTypeFor(graph, (Resource)input);\r
320             } else if (input instanceof Variable) {\r
321                 String uri = OntologyVersions.getInstance().currentVersion("http://www.simantics.org/Modeling-0.0/ModelingBrowseContext/Variable");\r
322                 return new SpecialNodeType(graph.getResource(uri), Variable.class);\r
323             }\r
324         }\r
325         return nodeType;\r
326     }\r
327     \r
328     /**\r
329      * Finds labels for the given {@link NodeContext} parameter.\r
330      * \r
331      * @param graph\r
332      * @param parent\r
333      * @return Map containing all the labels assigned by key indicating the column e.g. "single"\r
334      * @throws DatabaseException\r
335      */\r
336     public Map<String, String> getLabel(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
337         NodeType nodeType = getNodeType(graph, parent);\r
338         if(nodeType == null)\r
339             return Collections.singletonMap(ColumnKeys.SINGLE, "ERROR (no node type)");\r
340         List<LabelContribution> contributions = labelContributions.get(graph, nodeType); \r
341         for(LabelContribution contribution : contributions) { \r
342             Map<String, String> label = contribution.getLabel(graph, parent);\r
343             if(label != null)\r
344                 return label;\r
345         }\r
346         return Collections.singletonMap(ColumnKeys.SINGLE, "(no label rule)");\r
347     }\r
348 \r
349     /**\r
350      * Finds {@link ImageDescriptor}s for the given {@link NodeContext} parameter.\r
351      * \r
352      * @param graph\r
353      * @param parent\r
354      * @return Map containing all the {@ImageDescriptor}s or empty\r
355      * @throws DatabaseException\r
356      */\r
357     public Map<String, ImageDescriptor> getImage(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
358         NodeType nodeType = getNodeType(graph, parent);\r
359         if(nodeType == null)\r
360             return Collections.emptyMap();\r
361         for(ImageContribution contribution : imageContributions.get(graph, nodeType)) { \r
362             Map<String, ImageDescriptor> image = contribution.getImage(graph, parent);\r
363             if(image != null)\r
364                 return image;\r
365         }\r
366         return Collections.emptyMap();\r
367     }\r
368 \r
369     /**\r
370      * Finds if the given {@link NodeContext} is checked or not.\r
371      * \r
372      * @param graph\r
373      * @param parent\r
374      * @return\r
375      * @throws DatabaseException\r
376      */\r
377     public CheckedState getCheckedState(ReadGraph graph, NodeContext parent) throws DatabaseException {\r
378         NodeType nodeType = getNodeType(graph, parent);\r
379         if(nodeType == null)\r
380             return CheckedState.NOT_CHECKED;\r
381         for(CheckedStateContribution contribution : checkedStateContributions.get(graph, nodeType)) { \r
382             CheckedState state = contribution.getCheckedState(graph, parent);\r
383             if(state != null)\r
384                 return state;\r
385         }\r
386         return CheckedState.NOT_CHECKED;\r
387     }\r
388 \r
389     /**\r
390      * Finds {@link LabelDecorator} for the given {@link NodeContext} parameter.\r
391      * \r
392      * @param graph\r
393      * @param context\r
394      * @return\r
395      * @throws DatabaseException\r
396      */\r
397     public LabelDecorator getLabelDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {\r
398         NodeType nodeType = getNodeType(graph, context);\r
399         if(nodeType == null)\r
400             return CompositeLabelDecorator.ID;\r
401         ArrayList<LabelDecorator> decorators = new ArrayList<LabelDecorator>();\r
402         for(LabelDecorationContribution contribution : labelDecorationContributions.get(graph, nodeType)) {\r
403             LabelDecorator decorator = contribution.getLabelDecorator(graph, context);\r
404             if(decorator != null)\r
405                 decorators.add(decorator);\r
406         }\r
407         return CompositeLabelDecorator.create(decorators);\r
408     }\r
409 \r
410     /**\r
411      * Finds {@link ImageDecorator} for the given {@link NodeContext} parameter.\r
412      * \r
413      * @param graph\r
414      * @param context\r
415      * @return\r
416      * @throws DatabaseException\r
417      */\r
418     public ImageDecorator getImageDecorator(ReadGraph graph, NodeContext context) throws DatabaseException {\r
419         NodeType nodeType = getNodeType(graph, context);\r
420         if(nodeType == null)\r
421             return CompositeImageDecorator.ID;\r
422         ArrayList<ImageDecorator> decorators = new ArrayList<ImageDecorator>();\r
423         for(ImageDecorationContribution contribution : imageDecorationContributions.get(graph, nodeType)) {\r
424             ImageDecorator decorator = contribution.getImageDecorator(graph, context);\r
425             if(decorator != null)\r
426                 decorators.add(decorator);\r
427         }\r
428         return CompositeImageDecorator.create(decorators);\r
429     }\r
430 \r
431     /**\r
432      * Finds {@link Modifier} for the given {@link NodeContext} parameter.\r
433      * \r
434      * @param graph\r
435      * @param context\r
436      * @param columnKey\r
437      * @return\r
438      * @throws DatabaseException\r
439      */\r
440     public Modifier getModifier(ReadGraph graph, NodeContext context,\r
441             String columnKey) throws DatabaseException {\r
442         NodeType nodeType = getNodeType(graph, context);\r
443         if(nodeType != null)\r
444             for(ModifierContribution contribution : modifierContributions.get(graph, nodeType)) { \r
445                 Modifier modifier = contribution.getModifier(graph, context, columnKey);\r
446                 if(modifier == NoModifierRule.NO_MODIFIER)\r
447                     return null;\r
448                 if(modifier != null)\r
449                     return modifier;\r
450             }\r
451         return null;\r
452     }\r
453     \r
454     public TooltipContribution shouldCreateToolTip(ReadGraph graph, Event event, NodeContext context) throws DatabaseException {\r
455         NodeType nodeType = getNodeType(graph, context);\r
456         if(nodeType != null)\r
457             for(TooltipContribution contribution : tooltipContributions.get(graph, nodeType)) { \r
458                 if (contribution.shouldCreateToolTip(graph, context))\r
459                     return contribution;\r
460             }\r
461         return null;\r
462     }\r
463     \r
464     public Object getTooltip(TooltipContribution contribution, Object event, Object parent, NodeContext context) throws DatabaseException {\r
465         Object tooltip = contribution.getTooltip(event, parent, context);\r
466         if (tooltip != null)\r
467             return tooltip;\r
468         return null;\r
469     }\r
470     \r
471     private Graph toGraph() {\r
472         Graph graph = new Graph();\r
473         new Node(graph, "Foo");\r
474         return graph;\r
475     }\r
476 \r
477     @SuppressWarnings("unused")\r
478     private void visualize() {\r
479         final Graph graph = toGraph();\r
480                 \r
481         // Show it\r
482         new Thread() {\r
483             public void run() {\r
484                 final Display display = new Display();\r
485                 final Shell shell = new Shell(display);\r
486 \r
487                 GraphvizComponent comp = new GraphvizComponent(shell, 0);\r
488                 comp.setGraph(graph);\r
489 \r
490                 comp.setBounds(0, 0, 800, 600);\r
491                 shell.pack();\r
492                 shell.open ();\r
493                 while (!shell.isDisposed()) {\r
494                     if (!display.readAndDispatch()) display.sleep();\r
495                 }\r
496                 display.dispose();\r
497             }\r
498         }.start();      \r
499     }\r
500 \r
501     @Override\r
502     public int hashCode() {\r
503         return Arrays.hashCode(uris);\r
504     }\r
505 \r
506     @Override\r
507     public boolean equals(Object obj) {\r
508         if (this == obj)\r
509             return true;\r
510         if (obj == null)\r
511             return false;\r
512         if (getClass() != obj.getClass())\r
513             return false;\r
514         BrowseContext other = (BrowseContext) obj;\r
515         return Arrays.equals(uris, other.uris);\r
516     }\r
517 \r
518     @Override\r
519     public String toString() {\r
520         return getClass().getSimpleName() + Arrays.toString(uris);\r
521     }\r
522 \r
523 }\r