Improves XML page & indexer : adds hyperlinks on Spring bean declarations

This commit is contained in:
emmanue1 2015-06-22 20:55:41 +02:00
parent 347799254c
commit 5ef246ff5e
9 changed files with 305 additions and 36 deletions

View File

@ -10,7 +10,7 @@ import jd.gui.api.model.Container
import jd.gui.api.model.Indexes
import jd.gui.util.xml.AbstractXmlPathFinder
class EjbJarXmlFileIndexerProvider extends XmlFileIndexerProvider {
class EjbJarXmlFileIndexerProvider extends XmlBasedFileIndexerProvider {
/**
* @return local + optional external selectors

View File

@ -10,7 +10,7 @@ import jd.gui.api.model.Container
import jd.gui.api.model.Indexes
import jd.gui.util.xml.AbstractXmlPathFinder
class WebXmlFileIndexerProvider extends XmlFileIndexerProvider {
class WebXmlFileIndexerProvider extends XmlBasedFileIndexerProvider {
/**
* @return local + optional external selectors

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2008-2015 Emmanuel Dupuy
* This program is made available under the terms of the GPLv3 License.
*/
package jd.gui.service.indexer
import groovy.transform.CompileStatic
import jd.gui.api.API
import jd.gui.api.model.Container
import jd.gui.api.model.Indexes
import javax.xml.stream.XMLInputFactory
import javax.xml.stream.XMLStreamConstants
class XmlBasedFileIndexerProvider extends AbstractIndexerProvider {
XMLInputFactory factory
XmlBasedFileIndexerProvider() {
factory = XMLInputFactory.newInstance()
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false)
}
/**
* @return local + optional external selectors
*/
String[] getSelectors() { ['*:file:*.xsl', '*:file:*.xslt', '*:file:*.xsd'] + externalSelectors }
@CompileStatic
void index(API api, Container.Entry entry, Indexes indexes) {
def stringSet = new HashSet<String>()
def reader
try {
reader = factory.createXMLStreamReader(entry.inputStream)
stringSet.add(reader.version)
stringSet.add(reader.encoding)
stringSet.add(reader.characterEncodingScheme)
while (reader.hasNext()) {
switch (reader.next()) {
case XMLStreamConstants.START_ELEMENT:
stringSet.add(reader.localName)
for (int i = reader.attributeCount - 1; i >= 0; i--) {
stringSet.add(reader.getAttributeLocalName(i))
stringSet.add(reader.getAttributeValue(i))
}
for (int i = reader.namespaceCount - 1; i >= 0; i--) {
stringSet.add(reader.getNamespacePrefix(i))
stringSet.add(reader.getNamespaceURI(i))
}
break
case XMLStreamConstants.PROCESSING_INSTRUCTION:
stringSet.add(reader.getPITarget())
stringSet.add(reader.getPIData())
break
case XMLStreamConstants.START_DOCUMENT:
stringSet.add(reader.version)
stringSet.add(reader.encoding)
stringSet.add(reader.characterEncodingScheme)
break
case XMLStreamConstants.ENTITY_REFERENCE:
stringSet.add(reader.localName)
stringSet.add(reader.text)
break
case XMLStreamConstants.ATTRIBUTE:
stringSet.add(reader.prefix)
stringSet.add(reader.namespaceURI)
stringSet.add(reader.localName)
stringSet.add(reader.text)
break
case XMLStreamConstants.COMMENT:
case XMLStreamConstants.DTD:
case XMLStreamConstants.CDATA:
case XMLStreamConstants.CHARACTERS:
stringSet.add(reader.text.trim())
break
case XMLStreamConstants.NAMESPACE:
for (int i = reader.namespaceCount - 1; i >= 0; i--) {
stringSet.add(reader.getNamespacePrefix(i))
stringSet.add(reader.getNamespaceURI(i))
}
break
}
}
} catch (Exception ignore) {
} finally {
reader?.close()
}
def stringIndex = indexes.getIndex('strings')
for (def string : stringSet) {
if (string) {
stringIndex.get(string).add(entry)
}
}
}
}

View File

@ -24,63 +24,73 @@ class XmlFileIndexerProvider extends AbstractIndexerProvider {
/**
* @return local + optional external selectors
*/
String[] getSelectors() { ['*:file:*.xml', '*:file:*.xsl', '*:file:*.xslt', '*:file:*.xsd'] + externalSelectors }
String[] getSelectors() { ['*:file:*.xml'] + externalSelectors }
@CompileStatic
void index(API api, Container.Entry entry, Indexes indexes) {
def index = indexes.getIndex('strings')
def set = new HashSet<String>()
def stringSet = new HashSet<String>()
def typeReferenceSet = new HashSet<String>()
def reader
try {
reader = factory.createXMLStreamReader(entry.inputStream)
set.add(reader.version)
set.add(reader.encoding)
set.add(reader.characterEncodingScheme)
stringSet.add(reader.version)
stringSet.add(reader.encoding)
stringSet.add(reader.characterEncodingScheme)
while (reader.hasNext()) {
switch (reader.next()) {
case XMLStreamConstants.START_ELEMENT:
set.add(reader.localName)
boolean beanFlag = reader.localName.equals('bean')
stringSet.add(reader.localName)
for (int i = reader.attributeCount - 1; i >= 0; i--) {
set.add(reader.getAttributeLocalName(i))
set.add(reader.getAttributeValue(i))
def attributeName = reader.getAttributeLocalName(i)
stringSet.add(attributeName)
if (beanFlag && attributeName.equals('class')) {
// String bean reference
typeReferenceSet.add(reader.getAttributeValue(i).replace('.', '/'))
} else {
stringSet.add(reader.getAttributeValue(i))
}
}
for (int i = reader.namespaceCount - 1; i >= 0; i--) {
set.add(reader.getNamespacePrefix(i))
set.add(reader.getNamespaceURI(i))
stringSet.add(reader.getNamespacePrefix(i))
stringSet.add(reader.getNamespaceURI(i))
}
break
case XMLStreamConstants.PROCESSING_INSTRUCTION:
set.add(reader.getPITarget())
set.add(reader.getPIData())
stringSet.add(reader.getPITarget())
stringSet.add(reader.getPIData())
break
case XMLStreamConstants.START_DOCUMENT:
set.add(reader.version)
set.add(reader.encoding)
set.add(reader.characterEncodingScheme)
stringSet.add(reader.version)
stringSet.add(reader.encoding)
stringSet.add(reader.characterEncodingScheme)
break
case XMLStreamConstants.ENTITY_REFERENCE:
set.add(reader.localName)
set.add(reader.text)
stringSet.add(reader.localName)
stringSet.add(reader.text)
break
case XMLStreamConstants.ATTRIBUTE:
set.add(reader.prefix)
set.add(reader.namespaceURI)
set.add(reader.localName)
set.add(reader.text)
stringSet.add(reader.prefix)
stringSet.add(reader.namespaceURI)
stringSet.add(reader.localName)
stringSet.add(reader.text)
break
case XMLStreamConstants.COMMENT:
case XMLStreamConstants.DTD:
case XMLStreamConstants.CDATA:
case XMLStreamConstants.CHARACTERS:
set.add(reader.text.trim())
stringSet.add(reader.text.trim())
break
case XMLStreamConstants.NAMESPACE:
for (int i = reader.namespaceCount - 1; i >= 0; i--) {
set.add(reader.getNamespacePrefix(i))
set.add(reader.getNamespaceURI(i))
stringSet.add(reader.getNamespacePrefix(i))
stringSet.add(reader.getNamespaceURI(i))
}
break
}
@ -90,9 +100,18 @@ class XmlFileIndexerProvider extends AbstractIndexerProvider {
reader?.close()
}
for (def string : set) {
def stringIndex = indexes.getIndex('strings')
def typeReferenceIndex = indexes.getIndex('typeReferences')
for (def string : stringSet) {
if (string) {
index.get(string).add(entry)
stringIndex.get(string).add(entry)
}
}
for (def ref : typeReferenceSet) {
if (ref) {
typeReferenceIndex.get(ref).add(entry)
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2008-2015 Emmanuel Dupuy
* This program is made available under the terms of the GPLv3 License.
*/
package jd.gui.service.treenode
import jd.gui.api.API
import jd.gui.api.feature.UriGettable
import jd.gui.api.model.Container
import jd.gui.view.data.TreeNodeBean
import org.fife.ui.rsyntaxtextarea.SyntaxConstants
import javax.swing.*
import javax.swing.tree.DefaultMutableTreeNode
class XmlBasedFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {
static final ImageIcon ICON = new ImageIcon(XmlBasedFileTreeNodeFactoryProvider.class.classLoader.getResource('images/xml_obj.gif'))
/**
* @return local + optional external selectors
*/
String[] getSelectors() { ['*:file:*.xsl', '*:file:*.xslt', '*:file:*.xsd', '*:file:*.tld', '*:file:*.wsdl'] + externalSelectors }
public <T extends DefaultMutableTreeNode & UriGettable> T make(API api, Container.Entry entry) {
int lastSlashIndex = entry.path.lastIndexOf('/')
def name = entry.path.substring(lastSlashIndex+1)
return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path"))
}
static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {
TreeNode(Container.Entry entry, Object userObject) {
super(entry, userObject)
}
public <T extends JComponent & UriGettable> T createPage(API api) {
return new TextFileTreeNodeFactoryProvider.Page(entry) {
String getSyntaxStyle() {
SyntaxConstants.SYNTAX_STYLE_XML
}
}
}
}
}

View File

@ -8,8 +8,8 @@ package jd.gui.service.treenode
import jd.gui.api.API
import jd.gui.api.feature.UriGettable
import jd.gui.api.model.Container
import jd.gui.view.component.XmlFilePage
import jd.gui.view.data.TreeNodeBean
import org.fife.ui.rsyntaxtextarea.SyntaxConstants
import javax.swing.*
import javax.swing.tree.DefaultMutableTreeNode
@ -20,7 +20,7 @@ class XmlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {
/**
* @return local + optional external selectors
*/
String[] getSelectors() { ['*:file:*.xml', '*:file:*.xsl', '*:file:*.xslt', '*:file:*.xsd', '*:file:*.tld', '*:file:*.wsdl'] + externalSelectors }
String[] getSelectors() { ['*:file:*.xml'] + externalSelectors }
public <T extends DefaultMutableTreeNode & UriGettable> T make(API api, Container.Entry entry) {
int lastSlashIndex = entry.path.lastIndexOf('/')
@ -34,11 +34,7 @@ class XmlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {
}
public <T extends JComponent & UriGettable> T createPage(API api) {
return new TextFileTreeNodeFactoryProvider.Page(entry) {
String getSyntaxStyle() {
SyntaxConstants.SYNTAX_STYLE_XML
}
}
return new XmlFilePage(api, entry)
}
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2008-2015 Emmanuel Dupuy
* This program is made available under the terms of the GPLv3 License.
*/
package jd.gui.view.component
import jd.gui.api.API
import jd.gui.api.feature.IndexesChangeListener
import jd.gui.api.feature.UriGettable
import jd.gui.api.model.Container
import jd.gui.api.model.Indexes
import org.fife.ui.rsyntaxtextarea.SyntaxConstants
import java.awt.*
import java.util.regex.Pattern
class XmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener {
protected API api
protected Container.Entry entry
protected Collection<Indexes> collectionOfIndexes
XmlFilePage(API api, Container.Entry entry) {
this.api = api
this.entry = entry
// Load content file
def text = entry.inputStream.text
// Create hyperlinks
def pattern = Pattern.compile('(?s)<\\s*bean[^<]+class\\s*=\\s*"([^"]*)"')
def matcher = text =~ pattern
while (matcher.find()) {
// Spring type reference found
def value = matcher.group(1)
def trim = value.trim()
if (trim) {
int startIndex = matcher.start(1) - 1
int endIndex = startIndex + value.length() + 2
def internalTypeName = trim.replace('.', '/')
addHyperlink(new TypeReferencePage.TypeHyperlinkData(startIndex, endIndex, internalTypeName))
}
}
// Display
setText(text)
// Show hyperlinks
indexesChanged(api.collectionOfIndexes)
}
String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_XML }
protected boolean isHyperlinkEnabled(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.enabled }
protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) {
if (hyperlinkData.enabled) {
// Save current position in history
def location = textArea.getLocationOnScreen()
int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int))
def uri = entry.uri
api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null))
// Open link
def internalTypeName = hyperlinkData.internalTypeName
def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(internalTypeName) }.flatten().grep { it!=null }
def rootUri = entry.container.root.uri.toString()
def sameContainerEntries = entries?.grep { it.uri.toString().startsWith(rootUri) }
if (sameContainerEntries) {
api.openURI(x, y, sameContainerEntries, null, hyperlinkData.internalTypeName)
} else if (entries) {
api.openURI(x, y, entries, null, hyperlinkData.internalTypeName)
}
}
}
// --- UriGettable --- //
URI getUri() { entry.uri }
// --- ContentSavable --- //
String getFileName() {
def path = entry.path
int index = path.lastIndexOf('/')
return path.substring(index+1)
}
// --- IndexesChangeListener --- //
void indexesChanged(Collection<Indexes> collectionOfIndexes) {
// Update the list of containers
this.collectionOfIndexes = collectionOfIndexes
// Refresh links
boolean refresh = false
for (def entry : hyperlinks.entrySet()) {
def data = entry.value
def internalTypeName = data.internalTypeName
boolean enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(internalTypeName) } != null
if (data.enabled != enabled) {
data.enabled = enabled
refresh = true
}
}
if (refresh) {
textArea.repaint()
}
}
}

View File

@ -6,4 +6,5 @@ jd.gui.service.indexer.MetainfServiceFileIndexerProvider
jd.gui.service.indexer.TextFileIndexerProvider
jd.gui.service.indexer.WebXmlFileIndexerProvider
jd.gui.service.indexer.ZipFileIndexerProvider
jd.gui.service.indexer.XmlBasedFileIndexerProvider
jd.gui.service.indexer.XmlFileIndexerProvider

View File

@ -23,5 +23,6 @@ jd.gui.service.treenode.WarPackageTreeNodeFactoryProvider
jd.gui.service.treenode.WebinfClassesDirectoryTreeNodeFactoryProvider
jd.gui.service.treenode.WebinfLibDirectoryTreeNodeFactoryProvider
jd.gui.service.treenode.WebXmlFileTreeNodeFactoryProvider
jd.gui.service.treenode.XmlBasedFileTreeNodeFactoryProvider
jd.gui.service.treenode.XmlFileTreeNodeFactoryProvider
jd.gui.service.treenode.ZipFileTreeNodeFactoryProvider