You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2010/11/18 00:54:58 UTC
svn commit: r1036290 -
/incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java
Author: marrs
Date: Wed Nov 17 23:54:58 2010
New Revision: 1036290
URL: http://svn.apache.org/viewvc?rev=1036290&view=rev
Log:
Still far from perfect, but we now have a simple UI to import bundles from the OBR into ACE. Selections and assocations are still a bit 'buggy' though.
Modified:
incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java
Modified: incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java?rev=1036290&r1=1036289&r2=1036290&view=diff
==============================================================================
--- incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java (original)
+++ incubator/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java Wed Nov 17 23:54:58 2010
@@ -18,20 +18,37 @@
*/
package org.apache.ace.webui.vaadin;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
import java.net.URL;
+import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import com.vaadin.ui.*;
-import org.apache.ace.client.repository.*;
-import org.apache.ace.client.repository.object.*;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.ace.client.repository.ObjectRepository;
+import org.apache.ace.client.repository.RepositoryAdmin;
+import org.apache.ace.client.repository.RepositoryAdminLoginContext;
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.SessionFactory;
+import org.apache.ace.client.repository.object.Artifact2GroupAssociation;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.GatewayObject;
+import org.apache.ace.client.repository.object.Group2LicenseAssociation;
+import org.apache.ace.client.repository.object.GroupObject;
+import org.apache.ace.client.repository.object.License2GatewayAssociation;
+import org.apache.ace.client.repository.object.LicenseObject;
import org.apache.ace.client.repository.repository.Artifact2GroupAssociationRepository;
import org.apache.ace.client.repository.repository.ArtifactRepository;
-import org.apache.ace.client.repository.repository.GatewayRepository;
import org.apache.ace.client.repository.repository.Group2LicenseAssociationRepository;
import org.apache.ace.client.repository.repository.GroupRepository;
import org.apache.ace.client.repository.repository.License2GatewayAssociationRepository;
@@ -45,6 +62,10 @@ import org.osgi.framework.InvalidSyntaxE
import org.osgi.service.log.LogService;
import org.osgi.service.useradmin.User;
import org.osgi.service.useradmin.UserAdmin;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
import com.vaadin.data.Item;
import com.vaadin.data.Property.ValueChangeEvent;
@@ -54,15 +75,20 @@ import com.vaadin.event.Transferable;
import com.vaadin.event.dd.DragAndDropEvent;
import com.vaadin.event.dd.DropHandler;
import com.vaadin.event.dd.TargetDetails;
-import com.vaadin.event.dd.acceptcriteria.AcceptAll;
import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
import com.vaadin.event.dd.acceptcriteria.Or;
import com.vaadin.terminal.Sizeable;
import com.vaadin.ui.AbstractSelect.AbstractSelectTargetDetails;
import com.vaadin.ui.AbstractSelect.VerticalLocationIs;
+import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.Table;
import com.vaadin.ui.Table.CellStyleGenerator;
import com.vaadin.ui.Table.TableTransferable;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
/*
@@ -99,7 +125,7 @@ public class VaadinClient extends com.va
private volatile ArtifactRepository m_artifactRepository;
private volatile GroupRepository m_featureRepository;
private volatile LicenseRepository m_distributionRepository;
- private volatile StatefulGatewayRepository m_targetRepository;
+ private volatile StatefulGatewayRepository m_statefulTargetRepository;
private volatile Artifact2GroupAssociationRepository m_artifact2GroupAssciationRepository;
private volatile Group2LicenseAssociationRepository m_group2LicenseAssociationRepository;
private volatile License2GatewayAssociationRepository m_license2GatewayAssociationRepository;
@@ -120,6 +146,7 @@ public class VaadinClient extends com.va
private Table m_activeTable;
private Set<?> m_activeSelection;
public SelectionListener m_activeSelectionListener;
+ private List<OBREntry> m_obrList;
// basic session ID generator
private static long generateSessionID() {
@@ -134,10 +161,10 @@ public class VaadinClient extends com.va
addDependency(component, LicenseRepository.class);
addDependency(component, ArtifactRepository.class);
addDependency(component, GroupRepository.class);
- addDependency(component, StatefulGatewayRepository.class);
addDependency(component, Artifact2GroupAssociationRepository.class);
addDependency(component, Group2LicenseAssociationRepository.class);
addDependency(component, License2GatewayAssociationRepository.class);
+ addDependency(component, StatefulGatewayRepository.class);
}
private void addDependency(Component component, Class service) {
@@ -195,7 +222,14 @@ public class VaadinClient extends com.va
m_artifactsPanel = createArtifactsPanel(main);
grid.addComponent(m_artifactsPanel, 0, 2);
- grid.addComponent(new Button("Add artifact..."), 0, 1);
+ Button addArtifactButton = new Button("Add artifact...");
+ addArtifactButton.addListener(new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ showAddArtifactDialog(main);
+ }
+ });
+
+ grid.addComponent(addArtifactButton, 0, 1);
m_featuresPanel = createFeaturesPanel(main);
grid.addComponent(m_featuresPanel, 1, 2);
@@ -211,10 +245,10 @@ public class VaadinClient extends com.va
grid.setRowExpandRatio(2, 1.0f);
- m_artifactsPanel.addListener(new SelectionListener(m_artifactsPanel, m_artifactRepository, new Class[] {}, new Class[] { GroupObject.class, LicenseObject.class, StatefulGatewayObject.class }, new Table[] { m_featuresPanel, m_distributionsPanel, m_targetsPanel }));
- m_featuresPanel.addListener(new SelectionListener(m_featuresPanel, m_featureRepository, new Class[] { ArtifactObject.class }, new Class[] { LicenseObject.class, StatefulGatewayObject.class }, new Table[] { m_artifactsPanel, m_distributionsPanel, m_targetsPanel }));
- m_distributionsPanel.addListener(new SelectionListener(m_distributionsPanel, m_distributionRepository, new Class[] { GroupObject.class, ArtifactObject.class }, new Class[] { StatefulGatewayObject.class }, new Table[] { m_artifactsPanel, m_featuresPanel, m_targetsPanel }));
- m_targetsPanel.addListener(new SelectionListener(m_targetsPanel, m_targetRepository, new Class[] { LicenseObject.class, GroupObject.class, ArtifactObject.class}, new Class[] {}, new Table[] { m_artifactsPanel, m_featuresPanel, m_distributionsPanel }));
+ m_artifactsPanel.addListener(new SelectionListener(m_artifactsPanel, m_artifactRepository, new Class[] {}, new Class[] { GroupObject.class, LicenseObject.class, GatewayObject.class }, new Table[] { m_featuresPanel, m_distributionsPanel, m_targetsPanel }));
+ m_featuresPanel.addListener(new SelectionListener(m_featuresPanel, m_featureRepository, new Class[] { ArtifactObject.class }, new Class[] { LicenseObject.class, GatewayObject.class }, new Table[] { m_artifactsPanel, m_distributionsPanel, m_targetsPanel }));
+ m_distributionsPanel.addListener(new SelectionListener(m_distributionsPanel, m_distributionRepository, new Class[] { GroupObject.class, ArtifactObject.class }, new Class[] { GatewayObject.class }, new Table[] { m_artifactsPanel, m_featuresPanel, m_targetsPanel }));
+ m_targetsPanel.addListener(new SelectionListener(m_targetsPanel, m_statefulTargetRepository, new Class[] { LicenseObject.class, GroupObject.class, ArtifactObject.class}, new Class[] {}, new Table[] { m_artifactsPanel, m_featuresPanel, m_distributionsPanel }));
m_artifactsPanel.setDropHandler(new AssociationDropHandler((Table) null, m_featuresPanel) {
@Override
@@ -245,13 +279,27 @@ public class VaadinClient extends com.va
@Override
protected void associateFromRight(String left, String right) {
- m_license2GatewayAssociationRepository.create(getDistribution(left), getTarget(right).getGatewayObject());
+ StatefulGatewayObject target = getTarget(right);
+ System.out.println("Target is " + target + " and is " + (target.isRegistered() ? "registered." : "not registered yet."));
+ if (!target.isRegistered()) {
+ target.register();
+ target.setAutoApprove(true);
+ }
+ m_license2GatewayAssociationRepository.create(getDistribution(left), target.getGatewayObject());
+ System.out.println("Associated!");
}
});
m_targetsPanel.setDropHandler(new AssociationDropHandler(m_distributionsPanel, (Table) null) {
@Override
protected void associateFromLeft(String left, String right) {
- m_license2GatewayAssociationRepository.create(getDistribution(left), getTarget(right).getGatewayObject());
+ StatefulGatewayObject target = getTarget(right);
+ System.out.println("Target is " + target + " and is " + (target.isRegistered() ? "registered." : "not registered yet."));
+ if (!target.isRegistered()) {
+ target.register();
+ target.setAutoApprove(true);
+ }
+ m_license2GatewayAssociationRepository.create(getDistribution(left), target.getGatewayObject());
+ System.out.println("Associated!");
}
@Override
@@ -607,14 +655,17 @@ public class VaadinClient extends com.va
}
private StatefulGatewayObject getTarget(String name) {
+ System.out.println("getTarget(" + name + ")");
try {
- List<StatefulGatewayObject> list = m_targetRepository.get(m_context.createFilter("(" + StatefulGatewayObject.KEY_ID + "=" + name + ")"));
+ List<StatefulGatewayObject> list = m_statefulTargetRepository.get(m_context.createFilter("(" + StatefulGatewayObject.KEY_ID + "=" + name + ")"));
if (list.size() == 1) {
+ System.out.println("getTarget returning " + list.get(0));
return list.get(0);
}
}
catch (InvalidSyntaxException e) {
}
+ System.out.println("getTarget returning NULL!!!!");
return null;
}
@@ -638,6 +689,7 @@ public class VaadinClient extends com.va
* Helper method to find all related {@link RepositoryObject}s in a given 'direction'
*/
private <FROM extends RepositoryObject, TO extends RepositoryObject> List<TO> getRelated(FROM from, Class<TO> toClass) {
+ // if the SGO is not backed by a GO yet, this will cause an exception
return from.getAssociations(toClass);
}
@@ -735,7 +787,8 @@ public class VaadinClient extends com.va
}; // add this to the others
licenseItem.getItemProperty("button").setValue(removeLinkButton);
}
- m_targets = m_targetRepository.get();
+ m_statefulTargetRepository.refresh();
+ m_targets = m_statefulTargetRepository.get();
m_targetsPanel.removeAllItems();
for (StatefulGatewayObject target : m_targets) {
Item targetItem = m_targetsPanel.addItem(target.getID());
@@ -758,6 +811,30 @@ public class VaadinClient extends com.va
}; // add this to the others
targetItem.getItemProperty("button").setValue(removeLinkButton);
}
+
+// m_targets = m_targetRepository.get();
+// m_targetsPanel.removeAllItems();
+// for (GatewayObject license : m_targets) {
+// Item targetItem = m_targetsPanel.addItem(license.getID());
+// targetItem.getItemProperty(OBJECT_NAME).setValue(license.getID());
+// targetItem.getItemProperty(OBJECT_DESCRIPTION).setValue("?");
+// Button removeLinkButton = new RemoveLinkButton<GatewayObject>(license, m_distributionsPanel, null) {
+// @Override
+// protected void removeLinkFromLeft(GatewayObject object, RepositoryObject other) {
+// List<License2GatewayAssociation> associations = object.getAssociationsWith((LicenseObject) other);
+// for (License2GatewayAssociation association : associations) {
+// System.out.println("> " + association.getLeft() + " <-> " + association.getRight());
+// m_license2GatewayAssociationRepository.remove(association);
+// }
+// m_associatedItems.remove(object);
+// }
+//
+// @Override
+// protected void removeLinkFromRight(GatewayObject object, RepositoryObject other) {
+// }
+// }; // add this to the others
+// targetItem.getItemProperty("button").setValue(removeLinkButton);
+// }
}
private abstract class RemoveLinkButton<REPO_OBJECT extends RepositoryObject> extends Button {
@@ -819,6 +896,15 @@ public class VaadinClient extends com.va
}
public void valueChange(ValueChangeEvent event) {
+
+ if (m_activeSelection != null && m_activeTable != null) {
+ for (Object val : m_activeSelection) {
+ m_activeTable.unselect(val);
+ }
+ }
+
+
+
m_activeSelectionListener = this;
// set the active table
@@ -836,6 +922,18 @@ public class VaadinClient extends com.va
// remember the active selection too
m_activeSelection = value;
+
+
+
+// for (Table t : m_tablesToRefresh) {
+// System.out.println("resetting selected items on other tables " + t);
+// t.setValue(null);
+// }
+
+
+
+
+
if (value == null) {
System.out.println("no selection");
}
@@ -847,50 +945,70 @@ public class VaadinClient extends com.va
for (Object val : value) {
System.out.println(" - " + m_table.getItem(val).getItemProperty(OBJECT_NAME) + " " + val);
RepositoryObject lo = lookup(val);
-
- List related = null;
- for (int i = 0; i < m_left.length; i++) {
- if (i == 0) {
- related = getRelated(lo, m_left[i]);
- System.out.println("left associated:");
- for (Object o : related) {
- System.out.println(" -> " + o);
+ System.out.println("lookup(" + val + ") returned " + lo);
+ if (lo != null) {
+ List related = null;
+ for (int i = 0; i < m_left.length; i++) {
+ if (i == 0) {
+ related = getRelated(lo, m_left[i]);
+ System.out.println("left associated:");
+ for (Object o : related) {
+ System.out.println(" -> " + o);
+ }
+ m_associatedItems.addAll(related);
}
- m_associatedItems.addAll(related);
- }
- else {
- related = getRelated(related, m_left[i]);
- System.out.println("left related:");
- for (Object o : related) {
- System.out.println(" -> " + o);
+ else {
+ related = getRelated(related, m_left[i]);
+ System.out.println("left related:");
+ for (Object o : related) {
+ System.out.println(" -> " + o);
+ }
+ m_relatedItems.addAll(related);
}
- m_relatedItems.addAll(related);
}
- }
- for (int i = 0; i < m_right.length; i++) {
- if (i == 0) {
- related = getRelated(lo, m_right[i]);
- System.out.println("right associated:");
- for (Object o : related) {
- System.out.println(" -> " + o);
+ for (int i = 0; i < m_right.length; i++) {
+ if (i == 0) {
+ related = getRelated(lo, m_right[i]);
+ System.out.println("right associated:");
+ for (Object o : related) {
+ System.out.println(" -> " + o);
+ }
+ m_associatedItems.addAll(related);
}
- m_associatedItems.addAll(related);
- }
- else {
- related = getRelated(related, m_right[i]);
- System.out.println("right related:");
- for (Object o : related) {
- System.out.println(" -> " + o);
+ else {
+ related = getRelated(related, m_right[i]);
+ System.out.println("right related:");
+ for (Object o : related) {
+ System.out.println(" -> " + o);
+ }
+ m_relatedItems.addAll(related);
}
- m_relatedItems.addAll(related);
}
}
+ System.out.println("summarizing associated:");
+ for (RepositoryObject ro : m_associatedItems) {
+ System.out.println("** " + ro);
+ }
+ System.out.println("summarizing related:");
+ for (RepositoryObject ro : m_relatedItems) {
+ System.out.println("** " + ro);
+ }
for (Table t : m_tablesToRefresh) {
System.out.println("refreshing " + t);
- t.setValue(null);
+// t.setValue(null);
t.requestRepaint();
}
+
+ System.out.println("summarizing associated:");
+ for (RepositoryObject ro : m_associatedItems) {
+ System.out.println("** " + ro);
+ }
+ System.out.println("summarizing related:");
+ for (RepositoryObject ro : m_relatedItems) {
+ System.out.println("** " + ro);
+ }
+
// when switching columns, we need to repaint, but it messes up the
// cursor position
// m_table.requestRepaint();
@@ -900,9 +1018,17 @@ public class VaadinClient extends com.va
public RepositoryObject lookup(Object value) {
for (RepositoryObject object : m_repository.get()) {
- if (getNamedObject(object).getName().equals(value)) {
- System.out.println("Found: " + getNamedObject(object).getName());
- return object;
+ System.out.println("..." + object);
+ if (object instanceof StatefulGatewayObject) {
+ object = ((StatefulGatewayObject) object).getGatewayObject();
+ }
+ NamedObject namedObject = getNamedObject(object);
+ System.out.println("..." + namedObject);
+ if (namedObject != null) {
+ if (namedObject.getName().equals(value)) {
+ System.out.println("Found: " + namedObject.getName());
+ return object;
+ }
}
}
return null;
@@ -1068,4 +1194,207 @@ public class VaadinClient extends com.va
throw new IllegalArgumentException();
}
}
+
+ private void showAddArtifactDialog(final Window main) {
+ final Window featureWindow = new Window();
+ featureWindow.setModal(true);
+ featureWindow.setCaption("Add artifact");
+ featureWindow.setWidth("50em");
+
+ // Configure the windws layout; by default a VerticalLayout
+ VerticalLayout layout = (VerticalLayout) featureWindow.getContent();
+ layout.setMargin(true);
+ layout.setSpacing(true);
+
+ final TextField search = new TextField("search");
+ final Table artifacts = new ArtifactTable(main);
+
+ search.setValue("");
+ try {
+ getBundles(artifacts);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ layout.addComponent(search);
+ layout.addComponent(artifacts);
+
+ Button close = new Button("Add", new Button.ClickListener() {
+ // inline click-listener
+ public void buttonClick(ClickEvent event) {
+ // close the window by removing it from the parent window
+ (featureWindow.getParent()).removeWindow(featureWindow);
+ // TODO add the selected artifacts
+ for (Object id : artifacts.getItemIds()) {
+ if (artifacts.isSelected(id)) {
+ for (OBREntry e : m_obrList) {
+ if (e.getUri().equals(id)) {
+ System.out.println("Importing " + e);
+ try {
+ importBundle(e);
+ }
+ catch (Exception e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ updateTableData();
+ }
+ });
+ // The components added to the window are actually added to the window's
+ // layout; you can use either. Alignments are set using the layout
+ layout.addComponent(close);
+ layout.setComponentAlignment(close, "right");
+
+ if (featureWindow.getParent() != null) {
+ // window is already showing
+ main.getWindow().showNotification("Window is already open");
+ } else {
+ // Open the subwindow by adding it to the parent
+ // window
+ main.getWindow().addWindow(featureWindow);
+ }
+ search.focus();
+ }
+
+ public static class ArtifactTable extends Table {
+ public ArtifactTable(final Window main) {
+ super("Artifacts");
+ addContainerProperty("symbolic name", String.class, null);
+ addContainerProperty("version", String.class, null);
+ setSizeFull();
+ setSelectable(true);
+ setMultiSelect(true);
+ setImmediate(true);
+ }
+ }
+
+ public void getBundles(Table table) throws Exception {
+ getBundles(table, "http://localhost:8080/obr/");
+ }
+
+ public void getBundles(Table table, String obrBaseUrl) throws Exception {
+ URL obrBase = new URL(obrBaseUrl);
+
+ // retrieve the repository.xml as a stream
+ URL url = null;
+ try {
+ url = new URL(obrBase, "repository.xml");
+ }
+ catch (MalformedURLException e) {
+ System.err.println("Error retrieving repository.xml from " + obrBase);
+ throw e;
+ }
+
+ InputStream input = null;
+ NodeList resources = null;
+ try {
+ URLConnection connection = url.openConnection();
+ connection.setUseCaches(false); //We always want the newest repository.xml file.
+ input = connection.getInputStream();
+
+ try {
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ // this XPath expressing will find all 'resource' elements which have an attribute 'uri'.
+ resources = (NodeList) xpath.evaluate("/repository/resource[@uri]", new InputSource(input), XPathConstants.NODESET);
+ }
+ catch (XPathExpressionException e) {
+ System.err.println("Error evaluating XPath expression.");
+ e.printStackTrace(System.err);
+ throw e;
+ }
+ }
+ catch (IOException e) {
+ e.printStackTrace(System.err);
+ throw e;
+ }
+ finally {
+ if (input != null) {
+ try {
+ input.close();
+ }
+ catch (IOException e) {
+ // too bad, no worries.
+ }
+ }
+ }
+
+ m_obrList = new ArrayList<OBREntry>();
+ for (int nResource = 0; nResource < resources.getLength(); nResource++) {
+ Node resource = resources.item(nResource);
+ NamedNodeMap attr = resource.getAttributes();
+ String uri = attr.getNamedItem("uri").getTextContent();
+ String symbolicname = attr.getNamedItem("symbolicname").getTextContent();
+ String version = attr.getNamedItem("version").getTextContent();
+ m_obrList.add(new OBREntry(symbolicname, version, uri));
+ }
+
+ // Create a list of filenames from the ArtifactRepository
+ List<OBREntry> fromRepository = new ArrayList<OBREntry>();
+ List<ArtifactObject> artifactObjects = m_artifactRepository.get();
+ artifactObjects.addAll(m_artifactRepository.getResourceProcessors());
+ for (ArtifactObject ao : artifactObjects) {
+ String artifactURL = ao.getURL();
+ if (artifactURL.startsWith(obrBase.toExternalForm())) {
+ // we now know this artifact comes from the OBR we are querying, so we are interested.
+ fromRepository.add(new OBREntry(ao.getName(), null /* TODO version */, new File(artifactURL).getName()));
+ }
+ }
+
+ // remove all urls we already know
+ m_obrList.removeAll(fromRepository);
+ if (m_obrList.isEmpty()) {
+ System.err.println("No data in obr...");
+ return;
+ }
+
+ // Create a list of all bundle names
+ for (OBREntry s : m_obrList) {
+ Item item = table.addItem(s.getUri());
+ item.getItemProperty("symbolic name").setValue(s.getSymbolicName());
+ item.getItemProperty("version").setValue(s.getVersion());
+ }
+ }
+
+ public void importBundle(OBREntry bundle) throws Exception {
+ m_artifactRepository.importArtifact(new URL(new URL(obr), bundle.getUri()), false);
+ }
+
+ public static class OBREntry {
+ private final String m_symbolicName;
+ private final String m_version;
+ private final String m_uri;
+
+ public OBREntry(String symbolicName, String version, String uri) {
+ m_symbolicName = symbolicName;
+ m_version = version;
+ m_uri = uri;
+ }
+
+ public String getVersion() {
+ return m_version;
+ }
+
+ public String getSymbolicName() {
+ return m_symbolicName;
+ }
+
+ public String getUri() {
+ return m_uri;
+ }
+
+ @Override
+ public int hashCode() {
+ return m_uri.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return m_uri.equals(((OBREntry) obj).m_uri);
+ }
+ }
}
\ No newline at end of file