diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.xtend index de07c01e..5126486a 100644 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.xtend +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.xtend @@ -14,6 +14,11 @@ */ package org.utplsql.sqldev.model +import java.io.StringWriter +import javax.xml.transform.OutputKeys +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult import javax.xml.xpath.XPathConstants import javax.xml.xpath.XPathFactory import org.w3c.dom.Node @@ -34,4 +39,29 @@ class XMLTools { val Node node = expr.evaluate(doc, XPathConstants.NODE) as Node return node } -} \ No newline at end of file + + def void trimWhitespace(Node node) { + val children = node.childNodes + for (i : 0 ..< children.length) { + val child = children.item(i) + if (child.nodeType == Node.TEXT_NODE) { + child.textContent = child.textContent.trim + } + trimWhitespace(child); + } + } + + def nodeToString(Node node, String cdataSectionElements) { + node.trimWhitespace + val writer = new StringWriter() + val factory = TransformerFactory.newInstance().newTransformer() + factory.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes") + factory.setOutputProperty(OutputKeys.INDENT, "yes") + factory.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3"); + factory.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, cdataSectionElements) + factory.transform(new DOMSource(node), new StreamResult(writer)) + val result = writer.toString() + val fixedResult = result.replaceAll('''''',"") + return fixedResult + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/snippet/SnippetMerger.xtend b/sqldev/src/main/java/org/utplsql/sqldev/snippet/SnippetMerger.xtend new file mode 100644 index 00000000..af3a7bb7 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/snippet/SnippetMerger.xtend @@ -0,0 +1,90 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.snippet + +import java.io.BufferedReader +import java.io.File +import java.io.IOException +import java.io.InputStreamReader +import java.io.StringReader +import java.nio.charset.Charset +import java.nio.file.Files +import java.nio.file.Paths +import java.util.stream.Collectors +import javax.xml.parsers.DocumentBuilderFactory +import oracle.dbtools.util.Resource +import org.utplsql.sqldev.model.XMLTools +import org.xml.sax.InputSource + +class SnippetMerger { + val extension XMLTools xmlTools = new XMLTools + File userSnippetsFile + String utplsqlSnippets + + def getUtplsqlSnippetsAsString() throws IOException { + val stream = class.getResourceAsStream("/org/utplsql/sqldev/resources/UtplsqlSnippets.xml") + val reader = new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset)) + return reader.lines.collect(Collectors.joining(System.lineSeparator)) + } + + new() { + // works in SQL Developer only, otherwise a ExceptionInInitializerError is thrown + this (new File(Resource.RAPTOR_USER.absolutePath + File.separator + "UserSnippets.xml")) + } + + new(File file) { + utplsqlSnippets = utplsqlSnippetsAsString + userSnippetsFile = file + } + + def merge() { + var String result + if (userSnippetsFile.exists) { + // file exists, proper merge required + val userSnippets = new String(Files.readAllBytes(Paths.get(userSnippetsFile.absolutePath))) + val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() + val userSnippetsDoc = docBuilder.parse(new InputSource(new StringReader(userSnippets))) + val userSnippetsGroups = userSnippetsDoc.getNodeList('''/snippets/group[not(@category="utPLSQL Annotations" or @category="utPLSQL Expectations")]''') + val utplsqlSnippetsDoc = docBuilder.parse(new InputSource(new StringReader(utplsqlSnippets))) + val utplsqlSnippetsGroups = utplsqlSnippetsDoc.getNodeList('''/snippets/group''') + result = ''' + + + «FOR i : 0 ..< userSnippetsGroups.length» + «userSnippetsGroups.item(i).nodeToString("code")» + «ENDFOR» + «FOR i : 0 ..< utplsqlSnippetsGroups.length» + «utplsqlSnippetsGroups.item(i).nodeToString("code")» + «ENDFOR» + + ''' + } else { + // just copy + result = utplsqlSnippets + + } + Files.write(Paths.get(userSnippetsFile.absolutePath), result.bytes) + } + + def getTemplate() { + return utplsqlSnippets + } + + def getFile() { + return userSnippetsFile + } + +} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.xtend index 9633fac0..9425bb98 100644 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.xtend +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.xtend @@ -20,6 +20,7 @@ import java.awt.event.ActionListener import java.util.Map import javax.swing.JButton import javax.swing.JCheckBox +import javax.swing.JOptionPane import javax.swing.JPanel import javax.swing.JSpinner import javax.swing.JTabbedPane @@ -33,6 +34,7 @@ import oracle.ide.panels.TraversalException import oracle.javatools.ui.layout.FieldLayoutBuilder import org.utplsql.sqldev.model.preference.PreferenceModel import org.utplsql.sqldev.resources.UtplsqlResources +import org.utplsql.sqldev.snippet.SnippetMerger import org.utplsql.sqldev.ui.common.DirectoryChooser class PreferencePanel extends DefaultTraversablePanel { @@ -43,6 +45,7 @@ class PreferencePanel extends DefaultTraversablePanel { val JCheckBox clearScreenCheckBox = new JCheckBox val JCheckBox autoExecuteCheckBox = new JCheckBox val JCheckBox checkRunUtplsqlTestCheckBox = new JCheckBox + val JButton importSnippetsButton = new JButton(UtplsqlResources.getString("PREF_IMPORT_SNIPPETS_BUTTON_LABEL")) val JPanel realtimeReporterPanel = new JPanel val SpinnerNumberModel numberOfRunsInHistoryModel = new SpinnerNumberModel(1, 1, 100, 1); val JSpinner numberOfRunsInHistorySpinner = new JSpinner(numberOfRunsInHistoryModel); @@ -101,6 +104,7 @@ class PreferencePanel extends DefaultTraversablePanel { runTab.add( runTab.field.label.withText(UtplsqlResources.getString("PREF_CHECK_RUN_UTPLSQL_TEST_LABEL")).component( checkRunUtplsqlTestCheckBox)) + runTab.addRow(importSnippetsButton) runTab.addVerticalSpring // realtime reporter group @@ -200,6 +204,13 @@ class PreferencePanel extends DefaultTraversablePanel { builder.addVerticalField("", tabbedPane) builder.addVerticalSpring + // register action listener for import snippets button + importSnippetsButton.addActionListener(new ActionListener() { + override actionPerformed(ActionEvent event) { + importSnippets + } + }) + // register action listener for create code template button createCodeTemplatesButton.addActionListener(new ActionListener() { override actionPerformed(ActionEvent event) { @@ -216,6 +227,15 @@ class PreferencePanel extends DefaultTraversablePanel { }) } + private def importSnippets() { + val snippetMerger = new SnippetMerger + snippetMerger.merge + val file = snippetMerger.file.absolutePath + val message = String.format(UtplsqlResources.getString("PREF_CONFIRM_IMPORT_MESSAGE"), file) + JOptionPane.showMessageDialog(null, message, UtplsqlResources.getString("PREF_CONFIRM_IMPORT_TITLE"), + JOptionPane.INFORMATION_MESSAGE); + } + private def loadCodeTemplates() { val Map map = CodeTemplateUtil.loadFiles() for (key : map.keySet) { diff --git a/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources.properties b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources.properties index 2cf4c1f8..8d12bcc5 100644 --- a/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources.properties +++ b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources.properties @@ -31,6 +31,7 @@ PREF_RESET_PACKAGE_LABEL=Reset package before running utPLSQL? PREF_CLEAR_SCREEN_LABEL=Clear script output panel before running utPLSQL? PREF_AUTO_EXECUTE_LABEL=Execute unit test automatically? PREF_CHECK_RUN_UTPLSQL_TEST_LABEL=Check availability of menu option? +PREF_IMPORT_SNIPPETS_BUTTON_LABEL=Import Snippets MENU_REALTIME_REPORTER_LABEL=Realtime Reporter PREF_NUMBER_OF_RUNS_IN_HISTORY_LABEL=Number of runs in history PREF_SHOW_DISABLED_COUNTER_LABEL=Show disabled counter? @@ -56,6 +57,8 @@ PREF_GENERATE_FILES_LABEL=Generate files? PREF_OUTPUT_DIRECTORY_LABEL=Output directory PREF_OUTPUT_DIRECTORY_BUTTON_LABEL=Browse PREF_DELETE_EXISTING_FILES_LABEL=Delete existing files in output directory? +PREF_CONFIRM_IMPORT_TITLE=Snippets imported +PREF_CONFIRM_IMPORT_MESSAGE=Snippets imported into %s. Please restart SQL Developer for this change to take effect. MENU_RUN_TEST_LABEL=Run utPLSQL test MENU_CODE_COVERAGE_LABEL=Code coverage... MENU_GENERATE_TEST_LABEL=Generate utPLSQL test diff --git a/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources_de.properties b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources_de.properties index d80965a5..eb86a295 100644 --- a/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources_de.properties +++ b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources_de.properties @@ -8,6 +8,7 @@ PREF_RESET_PACKAGE_LABEL=Package vor der Ausf PREF_CLEAR_SCREEN_LABEL=Skriptausgabe-Fenster vor der Ausfhrung von utPLSQL leeren? PREF_AUTO_EXECUTE_LABEL=Unit Test automatisch ausfhren? PREF_CHECK_RUN_UTPLSQL_TEST_LABEL=Verfgbarkeit der Menoption prfen? +PREF_IMPORT_SNIPPETS_BUTTON_LABEL=Code-Schnipsel importieren MENU_REALTIME_REPORTER_LABEL=Realtime Reporter PREF_NUMBER_OF_RUNS_IN_HISTORY_LABEL=Anzahl Ausfhrungen in der Historie PREF_SHOW_DISABLED_COUNTER_LABEL=Deaktiviert-Zhler anzeigen? @@ -33,6 +34,8 @@ PREF_GENERATE_FILES_LABEL=Dateien generieren? PREF_OUTPUT_DIRECTORY_LABEL=Ausgabeverzeichnis PREF_OUTPUT_DIRECTORY_BUTTON_LABEL=Auswhlen PREF_DELETE_EXISTING_FILES_LABEL=Bestehende Dateien im Ausgabeverzeichnis lschen? +PREF_CONFIRM_IMPORT_TITLE=Code-Schnipsel importiert +PREF_CONFIRM_IMPORT_MESSAGE=Code-Schnipsel in %s importiert. Bitte starten Sie den SQL Developer neu, um diese nderung zu aktivieren. MENU_RUN_TEST_LABEL=utPLSQL Test ausfhren MENU_CODE_COVERAGE_LABEL=Codeabdeckung... MENU_GENERATE_TEST_LABEL=utPLSQL Test generieren diff --git a/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlSnippets.xml b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlSnippets.xml new file mode 100644 index 00000000..8915628f --- /dev/null +++ b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlSnippets.xml @@ -0,0 +1,262 @@ + + + + + + )]]> + + + + + )]]> + + + + + )]]> + + + + + )]]> + + + + + [,...])]]> + + + + + + + + + + + + + + + + + + + + + + + + + .].][,...])]]> + + + + + .].][,...])]]> + + + + + .].][,...])]]> + + + + + .].][,...])]]> + + + + + .].][,...])]]> + + + + + .].][,...])]]> + + + + + )]]> + + + + + + + + + + )]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/SnippetTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/SnippetTest.xtend new file mode 100644 index 00000000..1a7e2387 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/SnippetTest.xtend @@ -0,0 +1,89 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test + +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths +import org.junit.Assert +import org.junit.Test +import org.utplsql.sqldev.snippet.SnippetMerger + +class SnippetTest { + + @Test + def void mergeAsCopy() { + val file = new File(System.getProperty("user.home") + File.separator + "UserSnippets.xml") + file.delete + val merger = new SnippetMerger(file) + merger.merge + Assert.assertTrue(file.exists) + val userSnippetsXml = new String(Files.readAllBytes(Paths.get(file.absolutePath))) + Assert.assertEquals(merger.template, userSnippetsXml ) + } + + @Test + def void mergeKeepExisting() { + val file = new File(System.getProperty("user.home") + File.separator + "UserSnippets.xml") + file.delete + val userSnippetsXml = ''' + + + + + + + + + + + '''.toString + Files.write(Paths.get(file.absolutePath), userSnippetsXml.bytes) + val merger = new SnippetMerger(file) + merger.merge + Assert.assertTrue(file.exists) + val userSnippetsXml2 = new String(Files.readAllBytes(Paths.get(file.absolutePath))) + Assert.assertTrue(userSnippetsXml2.length > userSnippetsXml.length) + Assert.assertTrue(userSnippetsXml2.contains('''''')) + Assert.assertTrue(userSnippetsXml2.contains('''''')) + Assert.assertTrue(userSnippetsXml2.contains('''''')) + } + + @Test + def void mergeRemoveExisting() { + val file = new File(System.getProperty("user.home") + File.separator + "UserSnippets.xml") + file.delete + val userSnippetsXml = ''' + + + + + + '''.toString + Files.write(Paths.get(file.absolutePath), userSnippetsXml.bytes) + val merger = new SnippetMerger(file) + merger.merge + Assert.assertTrue(file.exists) + val userSnippetsXml2 = new String(Files.readAllBytes(Paths.get(file.absolutePath))) + Assert.assertTrue(userSnippetsXml2.length > userSnippetsXml.length) + Assert.assertFalse(userSnippetsXml2.contains('''''')) + Assert.assertFalse(userSnippetsXml2.contains('''''')) + Assert.assertTrue(userSnippetsXml2.contains('''''')) + Assert.assertTrue(userSnippetsXml2.contains('''''')) + } + + +}