[MOBY-guts] biomoby commit
Paul Gordon
gordonp at dev.open-bio.org
Mon Mar 29 19:54:36 UTC 2010
gordonp
Mon Mar 29 15:54:36 EDT 2010
Update of /home/repository/moby/moby-live/Java/src/main/ca/ucalgary/seahawk/gui
In directory dev.open-bio.org:/tmp/cvs-serv10568/src/main/ca/ucalgary/seahawk/gui
Modified Files:
MobyContentPane.java
Log Message:
Filter update
moby-live/Java/src/main/ca/ucalgary/seahawk/gui MobyContentPane.java,1.22,1.23
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/ca/ucalgary/seahawk/gui/MobyContentPane.java,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- /home/repository/moby/moby-live/Java/src/main/ca/ucalgary/seahawk/gui/MobyContentPane.java 2010/03/17 20:21:55 1.22
+++ /home/repository/moby/moby-live/Java/src/main/ca/ucalgary/seahawk/gui/MobyContentPane.java 2010/03/29 19:54:36 1.23
@@ -12,6 +12,7 @@
import org.biomoby.client.MobyRequest;
import org.biomoby.client.MobyRequestEvent;
import org.biomoby.client.MobyRequestEventHandler;
+import org.biomoby.registry.meta.Registry;
import org.biomoby.shared.*;
import org.biomoby.shared.data.*;
import org.biomoby.shared.parser.MobyTags;
@@ -23,13 +24,28 @@
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
-
import javax.xml.xpath.*;
import javax.swing.*;
import javax.swing.event.*;
-
-import java.awt.*;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.text.Highlighter;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Toolkit;
import java.awt.datatransfer.*;
import java.awt.print.*;
import java.awt.event.*;
@@ -37,6 +53,7 @@
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
+import java.util.regex.*;
/**
* Text area contained in a Seahawk GUI tab. Displays HTML, RTF, text etc. using a JEditorPane.
@@ -83,7 +100,16 @@
private int lastClickY = 1;
private boolean hasFailed = false;
private boolean isContentsXML = false;
- private DataRecorder dataRecorder;
+ private DataFlowRecorder dataRecorder;
+ private List<String> xPtrsReferencedInNextService;
+ private Map<String,String> filteredData; //Map<xptrOfFilteredData,>
+ private boolean filterChanged;
+ private Document currentDoc; //the doc DOM being actively filtered
+ private NodeList currentSelectedData = null;
+ private String currentSelectionXPath = null;
+ private List<String> filterableNodes = null; // List<xptr>
+ private List<AttributeSet> origStyles = null;
+ private boolean firstDocRendering = true;
// Next two items used for service wrapping
private JMenuItem createServicePopupItem;
@@ -114,8 +140,22 @@
private final static String wsServletPath = "/SOAPServlet";
private static Acme.Serve.Serve servletContainer;
private static Map<String,MobyContentGUI> id2GuiMap;
+ private static DocumentBuilder docBuilder;
+ private static XPathFactory xPathFactory;
+
+ static{
+ xPathFactory = XPathFactory.newInstance();
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ try{
+ docBuilder = dbf.newDocumentBuilder();
+ } catch(Exception e){
+ logger.warn("Cannot get XML parser from configuration", e);
+ }
+ }
- public MobyContentPane(MobyContentGUI cGUI, MobyServicesGUI sGUI, JTabbedPane parentComponent, DataRecorder recorder, JLabel statusBar){
+ public MobyContentPane(MobyContentGUI cGUI, MobyServicesGUI sGUI, JTabbedPane parentComponent, DataFlowRecorder recorder, JLabel statusBar){
tabbedPane = parentComponent;
status = statusBar;
contentGUI = cGUI;
@@ -158,6 +198,8 @@
historyTabLabels = new HashMap<URL,String>();
filterHistory = new HashMap<URL,FilterSearch>();
deletedFilters = new HashMap<URL,FilterSearch>();
+ xPtrsReferencedInNextService = new Vector<String>();
+ filteredData = new HashMap<String,String>();
ClassLoader cl = getClass().getClassLoader();
if(cl == null){
@@ -198,7 +240,7 @@
return tabbedPane;
}
- public DataRecorder getDataRecorder(){
+ public DataFlowRecorder getDataFlowRecorder(){
return dataRecorder;
}
@@ -391,7 +433,7 @@
}
}));
new Thread(){public void run(){srv.serve();}}.start();
-
+
PBERecorder recorder = new PBERecorder();
recorder.setGUIMap(id2GuiMap);
servlet.setRecorder(recorder);
@@ -400,6 +442,9 @@
srv.addServlet(servletPath, servlet); // our deployment
}
else if(contentGUI.getServletContainer().getServlet(servletPath) == null){
+ PBERecorder recorder = new PBERecorder();
+ recorder.setGUIMap(id2GuiMap);
+ servlet.setRecorder(recorder);
contentGUI.getServletContainer().addServlet(servletPath, servlet);
}
@@ -694,12 +739,21 @@
}
// If this page has a filter associated with it before reload it
try{
+ currentDoc = null; // get rid of previous in-memory doc use for interactive filtering, if any
+ currentSelectedData = null;
+ currentSelectionXPath = null;
+ filterableNodes = null;
+ origStyles = null;
+ firstDocRendering = true;
+ editorPane.getHighlighter().removeAllHighlights();
+
if(filterHistory.containsKey(url)){
setFilterVisible(true);
}
else{
setFilterVisible(false);
}
+ filterChanged = false;
} catch(Exception e){
logger.warn("Could not enable filter history for this document: "+e.getMessage(), e);
}
@@ -729,17 +783,69 @@
}
public boolean canGoForward(){
- return historyIndex < history.size()-1;
+ return historyIndex < history.size()-1 && isFilterForwardConsistent();
+ }
+
+ /**
+ * If the filter condition has changed since the next service was originally called,
+ * only allow navigation to that document (service results) again if the new filter allows
+ * the data used in the original call to pass (for logical dataflow consistency).
+ */
+ public boolean isFilterForwardConsistent(){
+ // If there is no forward doc, it is consistent I suppose
+ if(historyIndex == history.size()-1){
+ contentGUI.setForwardButtonToolTip(null);
+ return true;
+ }
+ // If no filter on current doc, we must be okay to go forward
+ if(getFilter() == null){
+ contentGUI.setForwardButtonToolTip(null);
+ return true;
+ }
+
+ // Get the data from this doc used as the input to the forward document
+ if(xPtrsReferencedInNextService == null){
+ try{
+ xPtrsReferencedInNextService = DataUtils.getInputXPtrs(history.elementAt(historyIndex+1), history.elementAt(historyIndex));
+ } catch(Exception e){
+ logger.error("Cannot get the provenance data from "+history.elementAt(historyIndex+1), e);
+ }
+
+ }
+ // See if all the particular data from this doc that was used in the next service is still avaialble after
+ // applying the current version of the filter
+ for(String usedxPtr: xPtrsReferencedInNextService){
+ for(String filteredXPtr: filteredData.keySet()){
+ if(usedxPtr.equals(filteredXPtr) ||
+ usedxPtr.startsWith(filteredXPtr+"/")){
+ // the data used in the next service is filtered in the current view, not consistent to allow forward button
+ // give the person a reason the forward is disabled
+ contentGUI.setForwardButtonToolTip("<html>Cannot go forward to next doc, the corresponding service<br>"+
+ "depended on input data that is now filtered. Reset the data filter<br>"+
+ "to be able to go forward again</html>");
+ return false;
+ }
+ }
+ }
+
+ // If they differ, check that the forward doc's input src is not filtered out currently
+ contentGUI.setForwardButtonToolTip(null);
+ return true;
}
public void goForward(){
if(canGoForward()){
+ if(filterChanged){
+ DataUtils.updateInputFilter(history.elementAt(historyIndex+1), history.elementAt(historyIndex), getFilter());
+ }
gotoURL(history.elementAt(++historyIndex), false);
+ xPtrsReferencedInNextService = null;
}
}
public void goBackward(){
if(canGoBack()){
+ xPtrsReferencedInNextService = null;
gotoURL(history.elementAt(--historyIndex), false);
}
}
@@ -889,7 +995,7 @@
}
try {
- URL outputURL = dataRecorder.saveOutputData(mre);
+ URL outputURL = DataUtils.saveOutputData(mre);
gotoURL(outputURL, true);
} catch (Exception e) {
failed("Could not write a local file");
@@ -1009,8 +1115,13 @@
logger.warn("Failed: could not get hyperlink host: " + targetURL);
}
else if(isMobyURL(targetURL)){
- status.setText("Retrieving service list for selected data");
- showMobyOptions(targetURL);
+ if(isLinkEnabled(targetURL)){
+ status.setText("Retrieving service list for selected data");
+ showMobyOptions(targetURL);
+ }
+ else{
+ status.setText("Change the data filter to re-enable this link");
+ }
}
// External, hopefully HTML link
else{
@@ -1023,7 +1134,12 @@
overHyperlink = true;
lastHyperlinkHovered = targetURL;
if(isMobyURL(targetURL)){
- status.setText("Click to discover more Moby services, or drag onto a Web form");
+ if(isLinkEnabled(targetURL)){
+ status.setText("Click to discover more Moby services, or drag onto a Web form");
+ }
+ else{
+ status.setText("This link is current disabled because of the data filter.");
+ }
}
else if(targetURL == null){
String desc = he.getDescription();
@@ -1042,6 +1158,19 @@
}
}
+ private boolean isLinkEnabled(URL url){
+ String ref = url.getRef();
+ if(ref == null || ref.length() == 0){
+ return true;
+ }
+ for(String disabledXPtr: filteredData.keySet()){
+ if(ref.equals(disabledXPtr) || ref.startsWith(disabledXPtr+"/")){
+ return false;
+ }
+ }
+ return true;
+ }
+
public MobyDataInstance getDraggedData(){
return urlToMobyData(lastHyperlinkDragged);
}
@@ -1054,9 +1183,8 @@
String docFragID = targetURL.getRef(); // We store the xpath in the anchor part of the URL
// Complex case, load the doc fragment from the MOBY XML source file
- if(docFragID != null && docFragID.length() > 0 && contentGUI.getDocumentBuilder() != null &&
- !targetURL.getHost().equals("moby")){
- return loadMobyDataFromXPointer(targetURL);
+ if(docFragID != null && docFragID.length() > 0 && !targetURL.getHost().equals("moby")){
+ return DataUtils.loadMobyDataFromXPointer(targetURL, filterHistory.get(getCurrentURL()));
}
// Simple case, build the object using the values encoded in the URL itself
@@ -1208,164 +1336,334 @@
return mobyData;
}
- protected MobyDataInstance loadMobyDataFromXPointer(URL targetURL){
- // Are we dealing with a simple object or a complex one?
- MobyDataInstance mobyData = null;
-
- // Find the DOM fragment corresponding to the MOBY ID anchor specified
- // in the link URL by using an XPath statement on the source MOBY doc
- //System.err.println("Retrieving complex object from XPointer " + targetURL);
-
- // A child Xpointer is of the form /1/2/1/1, specifying the DOM child
- // descent path from the root node to get to the target node. The
- // equivalent XPath is /*[1]/*[2]/*[1]/*[1]
- String childXPath = targetURL.getRef().replaceAll("/(\\d+)", "/*[$1]");
- URL currentURL = null;
- try{currentURL = new URL(targetURL.toString().replaceAll("#"+targetURL.getRef(), ""));}
- catch(Exception e){
- logger.error("Couldn't extract referenceless URL from " + targetURL);
- }
- // Build the DOM
- Element mobyDOMRoot = null;
- Document domDoc = null;
- try{
- domDoc = contentGUI.getDocumentBuilder().parse(targetURL.openStream());
- } catch(org.xml.sax.SAXException saxe){
- logger.error("The document defining the MOBY data " +
- "could not be parsed: " + saxe);
- return null;
- } catch(java.io.IOException ioe){
- logger.error("The document defining the MOBY data " +
- " could not be read (from " + targetURL + "): " + ioe);
+ /**
+ * Gets the filter criteria currently being applied to the document being viewed.
+ *
+ * @return null unless there is a currently a non-blank filter criteria
+ */
+ public FilterSearch getFilter(){
+ if(getCurrentURL() == null){
return null;
}
-
- if(domDoc != null){
- mobyDOMRoot = domDoc.getDocumentElement();
+ FilterSearch fs = filterHistory.get(getCurrentURL());
+ if(fs != null && fs.getFilterRegex().length() > 0){
+ return fs;
}
- if(mobyDOMRoot == null){
- logger.warn("Error: Could not get MOBY document as DOM from source URL "
- + targetURL + " (empty or malformed document?)");
- return null;
+ return null;
+ }
+
+ /**
+ * Determine the parts of the XML document matching the FilterSearch criteria, and filter the view accordingly.
+ */
+ public void applyFilter(boolean apply){
+ if(!filterChanged){
+ filterChanged = true;
}
-
- // Build and run the XPath statement
- Element mobyObject = null;
- NodeList idSearchResult = null;
- Object xobject = null;
- try{
- xobject = XPathFactory.newInstance().newXPath().evaluate(childXPath,
- new InputSource(targetURL.openStream()),
- XPathConstants.NODESET);
-
- // Check the results
- if(xobject != null){
- idSearchResult = (NodeList) xobject;
+ filteredData.clear();
+
+ if(currentDoc == null){ // parse the doc into memory if not already there
+ try{
+ currentDoc = docBuilder.parse(getCurrentURL().openStream());
+ } catch(Exception e){
+ logger.error("Could not parse Moby document " + getCurrentURL(), e);
+ return;
+ }
+ }
+ // Filter out any moby jobs, parameters, collection members or HAS members that aren't in the matchingNodes
+ if(filterableNodes == null){
+ filterableNodes = new Vector<String>();
+ NodeList nodes = currentDoc.getElementsByTagNameNS(MobyTags.MOBY_XML_NS, MobyTags.MOBYDATA);
+ for(int i = 0; i < nodes.getLength(); i++){
+ filterableNodes.add(getXPtr(nodes.item(i)));
+ addHASXPtrs(filterableNodes, (Element) nodes.item(i));
+ }
+
+ // Collections
+ nodes = currentDoc.getElementsByTagNameNS(MobyTags.MOBY_XML_NS, MobyTags.COLLECTION);
+ for(int i = 0; i < nodes.getLength(); i++){
+ String collectionXPtr = getXPtr(nodes.item(i));
+ NodeList collectionMembers = nodes.item(i).getChildNodes();
+ int nonElementCnt = 0;
+ for(int j = 0; j < collectionMembers.getLength(); j++){
+ if(!(collectionMembers.item(j) instanceof Element)){
+ nonElementCnt++;
+ continue;
+ }
+ filterableNodes.add(collectionXPtr+"/"+(j-nonElementCnt+1)+"/1");
+ }
+ }
+ }
+
+ if(!apply || !filterHistory.containsKey(getCurrentURL()) ||
+ (filterHistory.containsKey(getCurrentURL()) &&
+ filterHistory.get(getCurrentURL()).getFilterRegex().toString().length() == 0)){
+ // Show whole doc if no filter or if filter is blank, so nothing to do here
+ if(firstDocRendering){
+ firstDocRendering = false;
}
else{
- logger.warn("Could not find Moby object in document " + currentURL +
- ", referred to by the reference ID in Moby link " + targetURL +
- " (document changed?)");
- return null;
+ displayFilterEffect(); //need to cleanup previous state
}
+ return;
}
- catch(Exception e){// Syntax error
- logger.error("Error: Could not search Moby data instance for XPath " +
- childXPath + "):" + e);
- return null;
+ // Otherwise make a list of xpointers to data that should be grayed out
+ FilterSearch fs = filterHistory.get(getCurrentURL());
+ XPathOption xsel = fs.getSelectedXPath();
+
+ // Find all the matching data
+ XPath xpath = xPathFactory.newXPath();
+ // Find the applicable DOM nodes if not yet found, or if selection criteria have changed
+ if(currentSelectedData == null || currentSelectionXPath == null || !currentSelectionXPath.equals(xsel.getXPath())){
+ currentSelectionXPath = xsel.getXPath();
+ try{
+ currentSelectedData = (NodeList) xpath.evaluate(currentSelectionXPath, currentDoc, XPathConstants.NODESET);
+ } catch(Exception e){
+ logger.error("Could not evaluate XPath (" + xsel.getXPath() + ") over " + getCurrentURL(), e);
+ return;
+ }
+ // System.err.println("There are " + currentSelectedData.getLength() +
+ // " items selected in the document using XPath " + currentSelectionXPath);
}
- if(idSearchResult.getLength() == 0){
- logger.warn("Error: Could not find Moby data instance with XPath (" + childXPath +
- " in " + targetURL);
- return null;
+ // Find just the data subset also matching the regex
+ Map<String,Boolean> matchingXPtrs = new LinkedHashMap<String,Boolean>();
+ // Build the FSA only once, for efficiency
+ Pattern regex = Pattern.compile(fs.getFilterRegex().toString(), Pattern.MULTILINE);
+ for(int i = 0; i < currentSelectedData.getLength(); i++){
+ Node node = currentSelectedData.item(i);
+ if(node instanceof Element){
+ if(regex.matcher(((Element) node).getTextContent()).find()){
+ matchingXPtrs.put(getXPtr(node), Boolean.TRUE);
+ }
+ else{
+ matchingXPtrs.put(getXPtr(node), Boolean.FALSE);
+ }
+ //System.err.println("Adding " + getXPtr(node) + " as " + matchingXPtrs.get(getXPtr(node)));
+ }
+ else if(node instanceof Attr){
+ if(regex.matcher(((Attr) node).getValue()).find()){
+ // Mark the element to which the attribute belongs
+ matchingXPtrs.put(getXPtr(((Attr) node).getOwnerElement()), Boolean.TRUE);
+ }
+ else{
+ matchingXPtrs.put(getXPtr(((Attr) node).getOwnerElement()), Boolean.FALSE);
+ }
+ }
+ else{
+ logger.warn("Found filter xpath result item that was not an Element or Attribute as expected ("+
+ node.getClass().getName()+")");
+ }
}
- if(idSearchResult.getLength() > 1){
- logger.warn("Error: Moby data instance could not beresolved because " +
- "there are multiple elements with XPath " + childXPath + " in " +
- currentURL);
- return null;
+
+ Map<String,Boolean> parentMatchingXPtrs = new LinkedHashMap<String,Boolean>();
+ for(String currXPtr: filterableNodes){
+ for(Map.Entry<String,Boolean> matchingXPtr: matchingXPtrs.entrySet()){
+ if(matchingXPtr.getKey().startsWith(currXPtr+"/")){ // a parent of the matching data
+ // No positive example yet?
+ if(!parentMatchingXPtrs.containsKey(currXPtr) || !parentMatchingXPtrs.get(currXPtr).booleanValue()){
+ //System.err.println("Adding "+ matchingXPtr.getValue() + " for " + currXPtr);
+ parentMatchingXPtrs.put(currXPtr, matchingXPtr.getValue());
+ }
+ }
+ }
}
- if(!(idSearchResult.item(0) instanceof Element)){
- logger.warn("Error: Moby data instance with XPath " + childXPath + " in " +
- currentURL + " was not an element as required!");
- return null;
+
+ matchingXPtrs.putAll(parentMatchingXPtrs);
+ for(String currXPtr: matchingXPtrs.keySet()){
+ // Is part of the selection criteria, but doesn't match the regex
+ if(!matchingXPtrs.get(currXPtr).booleanValue()){
+ filteredData.put(currXPtr, "todo");
+ }
}
- mobyObject = (Element) idSearchResult.item(0);
+
+ //todo: filter objects without the HAS field at all...
- // Create the Java MOBY API object based on the linked document DOM fragment
- try{
- MobyDataInstance mdi = MobyDataObject.createInstanceFromDOM(mobyObject, SeahawkOptions.getRegistry());
- if(mdi instanceof MobyDataObject){
- mobyData = mdi;
+ // apply gray out in the document display
+ displayFilterEffect();
+ }
+
+ // If any moby object member is in a HAS relationship, add it to the list of filterable items
+ private void addHASXPtrs(List<String> filterList, Element object){
+ String tagName = object.getLocalName();
+ boolean isContainer = false;
+ MobyDataType mobyDataType = null;
+ if(tagName.equals(MobyTags.MOBYDATA) || tagName.equals(MobyTags.COLLECTION) || tagName.equals(MobyTags.SIMPLE) ||
+ tagName.equals(MobyTags.CROSSREFERENCE)){
+ isContainer = true;
+ }
+ else{
+ mobyDataType = MobyDataType.getDataType(tagName, SeahawkOptions.getRegistry());
+ if(mobyDataType == null){
+ logger.warn("Found datatype unknown to the registry ("+object.getLocalName()+")");
+ return;
}
- else if(mdi instanceof MobyDataObjectSet){
- mobyData = mdi;
+ }
+
+ NodeList members = object.getChildNodes();
+ for(int i = 0; i < members.getLength(); i++){
+ if(!(members.item(i) instanceof Element)){
+ continue;
+ }
+
+ Element member = (Element) members.item(i);
+ addHASXPtrs(filterList, member);
+
+ if(!isContainer){
+ String memberName = member.getAttribute(MobyTags.ARTICLENAME);
+ MobyRelationship membership = mobyDataType.getChild(memberName);
+ //System.err.println("Relationship for " + tagName + " member " + memberName +
+ // " is " + (membership == null ? null : membership.getRelationshipType()));
+ if(membership != null && membership.getRelationshipType() == Central.iHAS){
+ filterList.add(getXPtr(member));
+ }
}
- else{
- logger.warn("Error: Moby data instance retrieved with XPath " + childXPath + " in " +
- currentURL + " was not a primary MOBY input object as expected");
- return null;
- }
-
- // To avoid misinterpretation of primitive datatypes as Objects with any namespace in queries for
- // services, add a token namespace to get reasonable service results.
- if(mobyData instanceof MobyDataObject && ((MobyDataObject) mobyData).getPrimaryNamespace() == null){
- ((MobyDataObject) mobyData).setPrimaryNamespace(new MobyNamespace("unknown"));
- }
-
- // For workflow creation, etc. associate the potential input data with its source generalized XPath
- // Also, if there is currently a filter on the doc, store it too as useful info for workflow creation.
- StringBuilder userData = new StringBuilder();
- userData.append(targetURL.getProtocol()+":"+targetURL.getPath() +
- "#" + elementInContextToNameBasedXPath(mobyObject));
- // todo: get rid of ref part of url for lookup
- logger.warn("Looking up filter info for "+ currentURL);
- if(filterHistory.containsKey(currentURL)){
- FilterSearch fs = filterHistory.get(currentURL);
- logger.warn("Found filter info for "+ currentURL + ": " + fs);
- if(fs.getFilterRegex().length() > 0){
- XPathOption xsel = fs.getSelectedXPath();
- userData.append("\t"+fs.getFilterRegex()+"\t"+xsel.getXPath()+"\t"+xsel.getDesc());
- }
- }
- mobyData.setUserData(userData.toString());
- }
- catch(MobyException mobye){ // Logic error
- logger.error("Error: Could not construct Moby data instance from document fragment: " +
- mobye);
- mobye.printStackTrace();
}
-
- return mobyData;
}
- /**
- * Gets the filter criteria currently being applied to the document being viewed.
- *
- * @return null unless there is a currently a non-blank filter criteria
- */
- public FilterSearch getFilter(){
- if(getCurrentURL() == null){
- return null;
+ // Recursively ascend the DOM tree and find out our place in its branching structure
+ private String getXPtr(Node n){
+ if(n == null || n instanceof Document){
+ return "";
}
- FilterSearch fs = filterHistory.get(getCurrentURL());
- if(fs != null && fs.getFilterRegex().length() > 0){
- return fs;
+ Node parent = n.getParentNode();
+
+ NodeList children = parent.getChildNodes();
+ int nonElementCnt = 0;
+ for(int i = 0; i < children.getLength(); i++){
+ if(!(children.item(i) instanceof Element)){
+ nonElementCnt++;
+ continue;
+ }
+ if(n == children.item(i)){
+ return getXPtr(parent)+"/"+(i-nonElementCnt+1);
+ }
}
return null;
}
/**
- * Restrict the view of the XML document to the parts matching the FilterSearch criteria
+ * Gray out parts of the HTML view if they are part of the filtered data.
*/
- public void applyFilter(boolean apply){
- if(!apply || !filterHistory.containsKey(getCurrentURL()) ||
- (filterHistory.containsKey(getCurrentURL()) &&
- filterHistory.get(getCurrentURL()).getFilterRegex().toString().length() == 0)){
- // Show whole doc if no filter or if filter is blank
+ private void displayFilterEffect(){
+
+ HTMLDocument d = (HTMLDocument) editorPane.getStyledDocument();
+
+ if(origStyles == null){
+ // Store a copy of the doc's default rendering for all elements with xpointer
+ // (i.e. filterable elements) before doing any filter display mods,
+ // as this allows us to remove the filter easily without reloading the document.
+ origStyles = new Vector<AttributeSet>();
+ for(String filterable: filterableNodes){
+ javax.swing.text.Element el = d.getElement(filterable);
+ origStyles.add(d.getParagraphElement(el.getStartOffset()).getAttributes());
+ }
+ firstDocRendering = false;
+ }
+ // Restore the original document appearance before applying the current filter...
+ else{
+ Iterator<AttributeSet> origAttIter = origStyles.iterator();
+ // Restore text data
+ for(String filterable: filterableNodes){
+ javax.swing.text.AbstractDocument.AbstractElement el =
+ (javax.swing.text.AbstractDocument.AbstractElement) d.getElement(filterable);
+ javax.swing.text.SimpleAttributeSet plain = new javax.swing.text.SimpleAttributeSet();
+ d.setParagraphAttributes(el.getStartOffset(), el.getEndOffset()-el.getStartOffset()+1, origAttIter.next(), true);
+ }
+ // Restore hyperlinks to default (blue underlined) appearance
+ javax.swing.text.SimpleAttributeSet blueAttrSet = new javax.swing.text.SimpleAttributeSet();
+ blueAttrSet.addAttribute(StyleConstants.Foreground, Color.blue);
+ blueAttrSet.addAttribute(StyleConstants.Underline, Boolean.TRUE);
+ for(HTMLDocument.Iterator it = d.getIterator(HTML.Tag.A); it.isValid(); it.next()) {
+ String link = (String) it.getAttributes().getAttribute(HTML.Attribute.HREF);
+ if(link == null){
+ continue;
+ }
+ d.setCharacterAttributes(it.getStartOffset(), it.getEndOffset()-it.getStartOffset()+1, blueAttrSet, false);
+ }
+ }
+
+ javax.swing.text.SimpleAttributeSet grayAttrSet = new javax.swing.text.SimpleAttributeSet();
+ grayAttrSet.addAttribute(StyleConstants.Foreground, Color.lightGray);
+ grayAttrSet.addAttribute(StyleConstants.Underline, Boolean.FALSE);
+
+ // Gray out hyperlinks that are for filtered data or their children
+ for(HTMLDocument.Iterator it = d.getIterator(HTML.Tag.A); it.isValid(); it.next()) {
+ String link = (String) it.getAttributes().getAttribute(HTML.Attribute.HREF);
+ if(link == null || link.indexOf("#") == -1){
+ continue;
+ }
+ link = link.replaceFirst("^.*#(.*)$", "$1"); // just get the ref
+
+ for(Map.Entry<String,String> dataToFilter: filteredData.entrySet()){
+ if(link.equals(dataToFilter.getKey()) || link.startsWith(dataToFilter.getKey()+"/")){
+ d.setCharacterAttributes(it.getStartOffset(), it.getEndOffset()-it.getStartOffset()+1, grayAttrSet, false);
+ }
+ }
+ }
+
+ // Gray out the mismatched textual data
+ for(Map.Entry<String,String> dataToFilter: filteredData.entrySet()){
+
+ //System.err.println("Filtering "+dataToFilter.getKey());
+ javax.swing.text.AbstractDocument.AbstractElement htmlDiv =
+ (javax.swing.text.AbstractDocument.AbstractElement) d.getElement(dataToFilter.getKey());
+ if(htmlDiv == null){
+ logger.error("Could not find HTML element with expected id "+dataToFilter.getKey());
+ continue;
+ }
+ int areaStart = htmlDiv.getStartOffset();
+ int areaEnd = htmlDiv.getEndOffset();
+ d.setParagraphAttributes(areaStart, areaEnd-areaStart+1, grayAttrSet, false);
}
+
+ // Highlight the matches
+ Highlighter h = editorPane.getHighlighter();
+ h.removeAllHighlights(); //get rid of old highlights
+ FilterSearch fs = filterHistory.get(getCurrentURL());
+ int matchCount = 0;
+ if(fs != null && fs.getFilterRegex().length() > 0){
+ try{
+ javax.swing.text.LayeredHighlighter.LayerPainter painter =
+ new DefaultHighlighter.DefaultHighlightPainter(Color.yellow);
+ Pattern p = Pattern.compile(fs.getFilterRegex().toString(), Pattern.MULTILINE);
+ for(String filterable: filterableNodes){
+ javax.swing.text.Element el = d.getElement(filterable);
+ String fragment = d.getText(el.getStartOffset(),
+ el.getEndOffset() - el.getStartOffset());
+ Matcher matcher = p.matcher(fragment);
+ while (matcher.find()) {
+ h.addHighlight(el.getStartOffset() + matcher.start(),
+ el.getStartOffset() + matcher.end(),
+ painter);
+ matchCount++;
+ }
+ }
+ } catch(Exception e){
+ logger.error("Could not apply match highlights", e);
+ }
+ }
+ status.setText(" Found "+matchCount+" match"+(matchCount == 1 ? "": "es"));
+
+ // Update the ability to press the forward button, based on the new filter criteria
+ contentGUI.updateHistoryButtons();
+ }
+
+ private List<javax.swing.text.Element> getHyperlinkElements(javax.swing.text.Element el){
+ List<javax.swing.text.Element> links = new Vector<javax.swing.text.Element>();
+ for(int i = 0; i < el.getElementCount(); i++){
+ javax.swing.text.Element childEl = el.getElement(i);
+ System.err.println("Found child element " + childEl.getName());
+ if(HTML.Tag.A.equals(childEl.getName())){
+ links.add(childEl);
+ }
+ else{
+ for(int j = 0; j < childEl.getElementCount(); j++){
+ links.addAll(getHyperlinkElements(childEl.getElement(j)));
+ }
+ }
+ }
+ return links;
}
/**
@@ -1384,7 +1682,7 @@
filterHistory.put(getCurrentURL(), new FilterSearch(getCurrentURL(), contentGUI.getDocumentBuilder()));
}
}
- logger.warn("Setting filter to "+filterHistory.get(getCurrentURL()) + " for " +getCurrentURL());
+ logger.debug("Setting filter to "+filterHistory.get(getCurrentURL()) + " for " +getCurrentURL());
filterSearchWidget.setFilter(filterHistory.get(getCurrentURL()));
searchPanel.add(filterSearchWidget);
searchPanel.revalidate();
@@ -1396,7 +1694,7 @@
deletedFilters.put(getCurrentURL(), filterHistory.remove(getCurrentURL()));
}
filterSearchWidget.setFilter(null);
- applyFilter(false); // no filter GUI = no filter effect
+ if(canFilter()){applyFilter(false);} // no filter GUI = no filter effect
searchPanel.remove(filterSearchWidget);
searchPanel.revalidate();
revalidate();
@@ -1404,63 +1702,6 @@
}
/**
- * Generalizes the element instance into an XPath retrieving it and all
- * similarly nested elements (based on traversing the parent nodes and prepending their names)
- */
- protected String elementInContextToNameBasedXPath(Element targetElement){
- String xpath = "";
-
- for(Element currentElement = targetElement;
- currentElement != null;
- currentElement = (Element) currentElement.getParentNode()){
- String elName = currentElement.getLocalName();
- // top level parameters' names are important
- String articleName = currentElement.getAttributeNS(MobyTags.MOBY_XML_NS, MobyTags.ARTICLENAME);
- if(articleName == null || articleName.trim().length() == 0){
- articleName = currentElement.getAttribute(MobyTags.ARTICLENAME);
- }
-
- if(elName.equals(MobyTags.SIMPLE)){
- xpath = "/"+MobyTags.SIMPLE+"[@"+MobyTags.ARTICLENAME + " = '" +
- articleName+"']" +xpath;
- }
-
- else if(elName.equals(MobyTags.COLLECTION)){
- xpath = "/"+MobyTags.COLLECTION+"[@"+MobyTags.ARTICLENAME + " = '" +
- articleName +"']"+xpath;
- }
- else if(elName.equals(MobyTags.MOBYOBJECT)){
- // Obviously, assume a base object is interesting based on its namespace
- xpath = "/*[@"+MobyTags.OBJ_NAMESPACE + " = '" +
- currentElement.getAttribute(MobyTags.OBJ_NAMESPACE)+"']" + xpath;
- }
- else if(elName.equals(MobyTags.MOBYFLOAT) ||
- elName.equals(MobyTags.MOBYSTRING) ||
- elName.equals(MobyTags.MOBYBOOLEAN) ||
- elName.equals(MobyTags.MOBYINTEGER) ||
- elName.equals(MobyTags.MOBYDATETIME)){
- // Assume that it isn't the primitive type, but rather the articleName
- // that makes it appropriate to use.
- xpath = "/*[@"+MobyTags.ARTICLENAME + " = '" +
- articleName+"']" + xpath;
- }
- else{
- // It's a complex type. Assume the type is why we are picking it.
- // TODO: In future we may want to check if a parent type is allowed in
- // this slot, and generalize to that. May also want to check that there isn't more
- // than one member of this type at this level in a HAS relationship...
- // in that case we probably want to go by articleName to catch the full semantics
- xpath = "/"+elName + xpath;
- }
- if(!(currentElement.getParentNode() instanceof Element)){
- break; // reached document node
- }
- }
-
- return xpath;
- }
-
- /**
* This method tries to figure out what kinds of MOBY Data could be
* created from the string given. e.g. Keywords, DNASequence, etc.
*/
@@ -1638,14 +1879,13 @@
dragging = true; // so we don't export many times in a row while dragging
lastHyperlinkDragged = lastHyperlinkHovered;
status.setText("Drop the hyperlink onto a Web form field to populate it");
- System.err.println("Dragging "+lastHyperlinkDragged);
getTransferHandler().exportToClipboard(this,
Toolkit.getDefaultToolkit().getSystemClipboard(),
TransferHandler.COPY);
getTransferHandler().exportAsDrag(this, e, TransferHandler.COPY);
}
else if(!dragging){
- System.err.println("Dragging with no hyperlink activated");
+ //System.err.println("Dragging with no hyperlink activated");
}
}
More information about the MOBY-guts
mailing list