diff --git a/pom.xml b/pom.xml index 4a100ffec5..636cbbd89a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,13 +1,13 @@ - 4.0.0 + 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.5.12 - - + + org.springframework.boot + spring-boot-starter-parent + 2.7.12 + + org.corpus-tools annis @@ -19,396 +19,385 @@ scm:git:git@github.com:korpling/ANNIS.git https://github.com/korpling/ANNIS v4.5.3 - - - - Corpuslinguistic working group Humboldt University Berlin - http://www.linguistik.hu-berlin.de/institut/professuren/korpuslinguistik/ - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - UTF-8 - 1.8 - 8.14.3 - true - org.corpus_tools.annis.gui.AnnisUiApplication - 2.4.3 - 4.7.2 - server - 1.5.24 - 1.8.4 - 1.6.0 - - - - - vaadin-snapshots - https://oss.sonatype.org/content/repositories/vaadin-snapshots/ - - false - - - true - - - - vaadin-addons - https://maven.vaadin.com/vaadin-addons - - - - - - - src/main/resources - false - - - src/main/resources - true - - **/*.properties - - - - src/main/webapp - - - ${project.build.directory}/native/ - - - - - - - org.jacoco - jacoco-maven-plugin - 0.8.6 - - - VAADIN/** - - - - - pre-unit-test - - prepare-agent - - - jacoco.agent.arg - - - - report - verify - - report - - - - - - org.springframework.boot - spring-boot-maven-plugin - - ${start-class} - - - - repackage - - repackage - - - ${spring.profiles.active} - true - - - - - - org.corpus-tools - cff-maven-plugin - 0.5.0 - - ${project.basedir}/misc/cff-templates/CITATION_input.yml - false - - - - - - - - co.enear.maven.plugins - keepachangelog-maven-plugin - 2.1.1 - - - org.apache.maven.plugins - maven-release-plugin - - clean verify keepachangelog:release cff:create cff:third-party-folder scm:add scm:checkin - v@{project.version} - install - - - - org.apache.maven.plugins - maven-scm-plugin - 1.11.3 - - CHANGELOG.md,CITATION.cff,THIRD-PARTY/**,src/main/webapp/VAADIN/help/** - Files updated in release process - - - - com.vaadin - vaadin-maven-plugin - ${vaadin.version} - - - - update-theme - update-widgetset - compile - compile-theme - - - - - 9 - - - - - - org.antlr - antlr4-maven-plugin - ${antlr4.version} - - src/main/antlr4 - - - - - antlr4 - - - - - - - org.codehaus.mojo - buildnumber-maven-plugin - 1.4 - - - validate - - create - - - - - false - false - {0, time, yyyy-MM-dd_HH-mm-ss} - 10 - - - - org.apache.maven.plugins - maven-surefire-plugin - - ${jacoco.agent.arg} - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - com.googlecode.maven-download-plugin - download-maven-plugin - 1.6.0 - - - download-linux-binaries - generate-resources - - wget - - - https://github.com/korpling/graphANNIS/releases/download/v${graphannis.version}/graphannis-webservice - ${project.build.directory}/native/linux-x86-64/ - b36bc518fcdd4f72965b16abcfc4ff52a5ae46a4a6cfeaceb40462f115878018 - - - - download-windows-binaries - generate-resources - - wget - - - https://github.com/korpling/graphANNIS/releases/download/v${graphannis.version}/graphannis-webservice.exe - ${project.build.directory}/native/win32-x86-64/ - 834140be0fae70bce677ab5b73bc2f626f8d40845738f56e040c275438607232 - - - - download-mac-binaries - generate-resources - - wget - - - https://github.com/korpling/graphANNIS/releases/download/v${graphannis.version}/graphannis-webservice.osx - ${project.build.directory}/native/darwin/ - 0269bf0c2f5bd781292e0529f7aafaa822fd4455d9e9af19325f5001539f2b28 - - - - - - org.openapitools - openapi-generator-maven-plugin - 5.0.1 - - - - generate - - - + + + + Corpuslinguistic working group Humboldt University Berlin + http://www.linguistik.hu-berlin.de/institut/professuren/korpuslinguistik/ + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + UTF-8 + 1.8 + 8.14.3 + true + org.corpus_tools.annis.gui.AnnisUiApplication + 2.4.3 + 4.7.2 + server + 1.5.24 + 1.8.4 + + + + + vaadin-snapshots + https://oss.sonatype.org/content/repositories/vaadin-snapshots/ + + false + + + true + + + + vaadin-addons + https://maven.vaadin.com/vaadin-addons + + + + + + + src/main/resources + false + + + src/main/resources + true + + **/*.properties + + + + src/main/webapp + + + ${project.build.directory}/native/ + + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.6 + + + VAADIN/** + + + + + pre-unit-test + + prepare-agent + + + jacoco.agent.arg + + + + report + verify + + report + + + + + + org.springframework.boot + spring-boot-maven-plugin + + ${start-class} + + + + repackage + + repackage + + + ${spring.profiles.active} + true + + + + + + org.corpus-tools + cff-maven-plugin + 0.5.0 + + ${project.basedir}/misc/cff-templates/CITATION_input.yml + false + + + + + + + + co.enear.maven.plugins + keepachangelog-maven-plugin + 2.1.1 + + + org.apache.maven.plugins + maven-release-plugin + + clean verify keepachangelog:release cff:create cff:third-party-folder scm:add scm:checkin + v@{project.version} + install + + + + org.apache.maven.plugins + maven-scm-plugin + 1.11.3 + + CHANGELOG.md,CITATION.cff,THIRD-PARTY/**,src/main/webapp/VAADIN/help/** + Files updated in release process + + + + com.vaadin + vaadin-maven-plugin + ${vaadin.version} + + + + update-theme + update-widgetset + compile + compile-theme + + + + + 9 + + + + + + org.antlr + antlr4-maven-plugin + ${antlr4.version} + + src/main/antlr4 + + + + + antlr4 + + + + + + + org.codehaus.mojo + buildnumber-maven-plugin + 1.4 + + + validate + + create + + + + + false + false + {0, time, yyyy-MM-dd_HH-mm-ss} + 10 + + + + org.apache.maven.plugins + maven-surefire-plugin + + ${jacoco.agent.arg} + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + com.googlecode.maven-download-plugin + download-maven-plugin + 1.6.0 + + + download-linux-binaries + generate-resources + + wget + + + https://github.com/korpling/graphANNIS/releases/download/v${graphannis.version}/graphannis-webservice + ${project.build.directory}/native/linux-x86-64/ + b36bc518fcdd4f72965b16abcfc4ff52a5ae46a4a6cfeaceb40462f115878018 + + + + download-windows-binaries + generate-resources + + wget + + + https://github.com/korpling/graphANNIS/releases/download/v${graphannis.version}/graphannis-webservice.exe + ${project.build.directory}/native/win32-x86-64/ + 834140be0fae70bce677ab5b73bc2f626f8d40845738f56e040c275438607232 + + + + download-mac-binaries + generate-resources + + wget + + + https://github.com/korpling/graphANNIS/releases/download/v${graphannis.version}/graphannis-webservice.osx + ${project.build.directory}/native/darwin/ + 0269bf0c2f5bd781292e0529f7aafaa822fd4455d9e9af19325f5001539f2b28 + + + + + + org.openapitools + openapi-generator-maven-plugin + 6.6.0 + + + + generate + + + ${project.basedir}/src/main/resources/openapi.yml - java - org.corpus_tools.annis.api - org.corpus_tools.annis.api.model - true - ApiResponse.java,ApiException.java,ApiCallback.java,Pair.java,ApiClient.java,Configuration.java,ProgressRequestBody.java,JSON.java,Authentication.java,StringUtil.java,ApiKeyAuth.java,HttpBasicAuth.java,ProgressResponseBody.java,HttpBearerAuth.java - false - false - false - false - - true - java8 - - serializableModel=true - - - - - - - - - - - com.vaadin - vaadin-bom - ${vaadin.version} - pom - import - - - org.keycloak.bom - keycloak-adapter-bom - 11.0.2 - pom - import - - - - - - - - com.vaadin - vaadin-spring-boot-starter - + java + org.corpus_tools.annis.api + org.corpus_tools.annis.api.model + true + false + false + false + false + + true + webclient + java8 + false + true + + serializableModel=true + + + + + + + + + + + com.vaadin + vaadin-bom + ${vaadin.version} + pom + import + + + org.keycloak.bom + keycloak-adapter-bom + 11.0.2 + pom + import + + + + + + + + com.vaadin + vaadin-spring-boot-starter + - - com.vaadin - vaadin-push - + + com.vaadin + vaadin-push + - - com.vaadin - vaadin-compatibility-server - - - com.vaadin - vaadin-compatibility-themes - - - com.vaadin - vaadin-compatibility-client - provided - - - com.vaadin - vaadin-compatibility-client-compiled - + + com.vaadin + vaadin-compatibility-server + + + com.vaadin + vaadin-compatibility-themes + + + com.vaadin + vaadin-compatibility-client + provided + + + com.vaadin + vaadin-compatibility-client-compiled + - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + - - org.springframework.boot - spring-boot-starter-data-jpa - + + org.springframework.boot + spring-boot-starter-data-jpa + - - org.springframework.boot - spring-boot-starter-oauth2-client - - - - org.springframework.boot - spring-boot-configuration-processor - + + org.springframework.boot + spring-boot-starter-webflux + - - org.springframework.security - spring-security-crypto - - - - com.github.jjYBdx4IL.utils - swing-utils - 1.0 - + + org.springframework.boot + spring-boot-starter-oauth2-client + - - com.h2database - h2 - runtime - + + org.springframework.boot + spring-boot-configuration-processor + org.corpus-tools @@ -423,147 +412,156 @@ - - io.swagger - swagger-annotations - ${swagger-core-version} - + + org.springframework.security + spring-security-crypto + - - com.squareup.okhttp3 - okhttp - + + com.github.jjYBdx4IL.utils + swing-utils + 1.0 + - - com.squareup.okhttp3 - mockwebserver - test - + + com.h2database + h2 + 1.4.200 + + + io.swagger + swagger-annotations + ${swagger-core-version} + - - com.squareup.okhttp3 - logging-interceptor - + + com.squareup.okhttp3 + okhttp + - - com.google.code.gson - gson - - - io.gsonfire - gson-fire - ${gson-fire-version} - + + com.squareup.okhttp3 + mockwebserver + test + - - javax.ws.rs - jsr311-api - 1.1.1 - + + com.squareup.okhttp3 + logging-interceptor + - - - javax.interceptor - javax.interceptor-api - 1.2.2 - + + com.google.code.gson + gson + + + io.gsonfire + gson-fire + ${gson-fire-version} + + + javax.ws.rs + jsr311-api + 1.1.1 + - - com.github.vaadin4qbanos - jsclipboard - 1.0.12 - - - com.vaadin - vaadin-server - - - + + + javax.interceptor + javax.interceptor-api + 1.2.2 + - - net.sf.opencsv - opencsv - 2.3 - jar - - - com.auth0 - java-jwt - 3.10.3 - + + com.github.vaadin4qbanos + jsclipboard + 1.0.12 + + + com.vaadin + vaadin-server + + + + + net.sf.opencsv + opencsv + 2.3 + jar + - - net.sf.jung - jung-api - 2.0.1 - - - net.sf.jung - jung-graph-impl - 2.0.1 - + + com.auth0 + java-jwt + 3.10.3 + - - com.hp.gagawa - gagawa - 1.0.1 - compile - - - org.antlr - antlr4 - ${antlr4.version} - compile - + + net.sf.jung + jung-api + 2.0.1 + + + net.sf.jung + jung-graph-impl + 2.0.1 + - - org.antlr - antlr4-runtime - ${antlr4.version} - compile - + + com.hp.gagawa + gagawa + 1.0.1 + compile + - - org.jetbrains.kotlin - kotlin-stdlib - ${kotlin.version} - test - + + org.antlr + antlr4 + ${antlr4.version} + compile + - - org.apache.httpcomponents - httpclient - + + org.antlr + antlr4-runtime + ${antlr4.version} + compile + - - com.github.romankh3 - image-comparison - 4.3.0 - test - + + org.apache.httpcomponents + httpclient + - - org.jsoup - jsoup - 1.15.3 - + + com.github.romankh3 + image-comparison + 4.3.0 + test + - - org.aeonbits.owner - owner - 1.0.10 - + + org.jsoup + jsoup + 1.15.3 + - - org.apache.tika - tika-core - 2.7.0 - + + org.aeonbits.owner + owner + 1.0.10 + + + + org.apache.tika + tika-core + 2.7.0 + joda-time @@ -571,159 +569,164 @@ 2.10.6 - - com.fasterxml.jackson.dataformat - jackson-dataformat-toml - + + com.fasterxml.jackson.dataformat + jackson-dataformat-toml + - - org.apache.commons - commons-email - 1.5 - compile - + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + - - commons-io - commons-io - 2.7 - compile - - - + + org.apache.commons + commons-email + 1.5 + compile + + + + commons-io + commons-io + 2.7 + compile + + + org.apache.commons commons-text [1.10.0,) compile - - commons-codec - commons-codec - compile - + + commons-codec + commons-codec + compile + - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + - - com.google.guava - guava-gwt - 20.0 - + + com.google.guava + guava-gwt + 20.0 + - - com.google.code.findbugs - jsr305 - 3.0.2 - + + com.google.code.findbugs + jsr305 + 3.0.2 + - - com.github.mvysny.kaributesting - karibu-testing-v8 - 1.3.17 - test - + + com.github.mvysny.kaributesting + karibu-testing-v8 + 1.3.17 + test + - - - org.vaadin.addons - popupbutton - 3.0.0 - + + + org.vaadin.addons + popupbutton + 3.0.0 + - - org.vaadin.extension - GridScrollExtension - 2.5.1 - - - - - - coverage - - - - org.jacoco - jacoco-maven-plugin - - - - - - desktop - - desktop - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - BOOT-INF/classes/splashscreen.gif - - - - - - - - - mdbook - - - ${user.home}/.cargo/bin/mdbook - - - - - - - - org.codehaus.mojo - exec-maven-plugin - 3.0.0 - - - compile-online-help - compile - - exec - - - ${user.home}/.cargo/bin/mdbook - ${project.build.directory} - - build - -d - ${project.basedir}/src/main/webapp/VAADIN/help/ - ${project.basedir}/docs/online-help/ - - - - - - - - maven-clean-plugin - - - - ${project.basedir}/src/main/webapp/VAADIN/help/ - false - - - - - - - - + + org.vaadin.extension + GridScrollExtension + 2.5.1 + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + + + + desktop + + desktop + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + BOOT-INF/classes/splashscreen.gif + + + + + + + + + mdbook + + + ${user.home}/.cargo/bin/mdbook + + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + compile-online-help + compile + + exec + + + ${user.home}/.cargo/bin/mdbook + ${project.build.directory} + + build + -d + ${project.basedir}/src/main/webapp/VAADIN/help/ + ${project.basedir}/docs/online-help/ + + + + + + + + maven-clean-plugin + + + + ${project.basedir}/src/main/webapp/VAADIN/help/ + false + + + + + + + + diff --git a/src/main/java/org/corpus_tools/annis/gui/AnnisBaseUI.java b/src/main/java/org/corpus_tools/annis/gui/AnnisBaseUI.java index 22738ea0f6..d36b83bafc 100644 --- a/src/main/java/org/corpus_tools/annis/gui/AnnisBaseUI.java +++ b/src/main/java/org/corpus_tools/annis/gui/AnnisBaseUI.java @@ -39,6 +39,7 @@ import java.util.TreeSet; import org.apache.commons.io.filefilter.SuffixFileFilter; import org.apache.commons.lang3.StringUtils; +import org.corpus_tools.annis.gui.objects.InstanceConfig; import org.slf4j.LoggerFactory; import org.slf4j.bridge.SLF4JBridgeHandler; diff --git a/src/main/java/org/corpus_tools/annis/gui/AnnisUI.java b/src/main/java/org/corpus_tools/annis/gui/AnnisUI.java index 539e1615fd..ec1eadd37d 100644 --- a/src/main/java/org/corpus_tools/annis/gui/AnnisUI.java +++ b/src/main/java/org/corpus_tools/annis/gui/AnnisUI.java @@ -13,8 +13,6 @@ */ package org.corpus_tools.annis.gui; -import static org.corpus_tools.annis.gui.Helper.DEFAULT_CONFIG; - import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.vaadin.annotations.Push; @@ -27,7 +25,9 @@ import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinSession; import com.vaadin.shared.communication.PushMode; +import com.vaadin.shared.ui.ui.Transport; import com.vaadin.spring.annotation.SpringUI; +import com.vaadin.spring.navigator.SpringViewProvider; import com.vaadin.ui.Component; import java.io.IOException; import java.net.URI; @@ -35,24 +35,25 @@ import java.util.Optional; import java.util.UUID; import javax.servlet.ServletContext; -import org.corpus_tools.annis.ApiClient; import org.corpus_tools.annis.api.model.CorpusConfiguration; import org.corpus_tools.annis.gui.admin.AdminView; import org.corpus_tools.annis.gui.components.ExceptionDialog; +import org.corpus_tools.annis.gui.components.MainToolbar; +import org.corpus_tools.annis.gui.controller.QueryController; import org.corpus_tools.annis.gui.exporter.ExporterPlugin; import org.corpus_tools.annis.gui.objects.QueryUIState; import org.corpus_tools.annis.gui.query_references.UrlShortener; -import org.corpus_tools.annis.gui.querybuilder.QueryBuilderPlugin; +import org.corpus_tools.annis.gui.querybuilder.tiger.QueryBuilderPlugin; import org.corpus_tools.annis.gui.requesthandler.BinaryRequestHandler; -import org.corpus_tools.annis.gui.security.AuthenticationSuccessListener; -import org.corpus_tools.annis.gui.security.AutoTokenRefreshClient; import org.corpus_tools.annis.gui.security.SecurityConfiguration; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.VisualizerPlugin; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; import org.springframework.core.env.Environment; import org.springframework.core.env.Profiles; +import org.springframework.web.reactive.function.client.WebClient; /** * GUI for searching in corpora. @@ -62,7 +63,7 @@ @Theme("annis") @Widgetset("org.corpus_tools.annis.gui.widgets.gwt.AnnisWidgetSet") @SpringUI(path = "/*") -@Push(value = PushMode.AUTOMATIC) +@Push(value = PushMode.AUTOMATIC, transport = Transport.LONG_POLLING) public class AnnisUI extends CommonUI implements ErrorHandler, ViewChangeListener { private static final Profiles DESKTOP_PROFILES = Profiles.of("desktop & !test"); @@ -75,7 +76,8 @@ public class AnnisUI extends CommonUI implements ErrorHandler, ViewChangeListene private QueryController queryController; - private SearchView searchView; + @Autowired + SpringViewProvider viewProvider; @Autowired private List visualizerPlugins; @@ -92,7 +94,10 @@ public class AnnisUI extends CommonUI implements ErrorHandler, ViewChangeListene @Autowired private UIConfig config; - private final AuthenticationSuccessListener authListener; + @Autowired + private WebClient webClient; + + private SearchView searchView; private AdminView adminView; @@ -113,10 +118,8 @@ public class AnnisUI extends CommonUI implements ErrorHandler, ViewChangeListene */ private MainToolbar toolbar; - @Autowired - public AnnisUI(ServiceStarter serviceStarter, AuthenticationSuccessListener authListener) { - super("", serviceStarter, authListener); - this.authListener = authListener; + public AnnisUI(ServiceStarter serviceStarter) { + super("", serviceStarter); initTransients(); } @@ -130,11 +133,11 @@ public boolean beforeViewChange(ViewChangeEvent event) { // make sure the toolbar is removed from the old view searchView.setToolbar(null); adminView.setToolbar(null); - toolbar.setSidebar(null); + toolbar.setSearchView(null); if (event.getNewView() == searchView) { searchView.setToolbar(toolbar); - toolbar.setSidebar(searchView); + toolbar.setSearchView(searchView); toolbar.setNavigationTarget(MainToolbar.NavigationTarget.ADMIN, AnnisUI.this); } else if (event.getNewView() == adminView) { adminView.setToolbar(toolbar); @@ -182,12 +185,7 @@ public CorpusConfiguration getCorpusConfigWithCache(String corpus) { if (corpusConfigCache != null) { config = corpusConfigCache.getIfPresent(corpus); if (config == null) { - if (corpus.equals(DEFAULT_CONFIG)) { - config = Helper.getDefaultCorpusConfig(); - } else { - config = Helper.getCorpusConfig(corpus, AnnisUI.this); - } - + config = Helper.getCorpusConfig(corpus, AnnisUI.this).block(); corpusConfigCache.put(corpus, config); } } @@ -227,11 +225,11 @@ protected void init(VaadinRequest request) { setErrorHandler(this); adminView = new AdminView(AnnisUI.this); + searchView = new SearchView(AnnisUI.this); toolbar = new MainToolbar(getConfig(), oauth2Clients); toolbar.setQueryController(queryController); - this.searchView = new SearchView(this); this.queryController = new QueryController(this, searchView, queryState); toolbar.addLoginListener(searchView); @@ -246,10 +244,10 @@ protected void init(VaadinRequest request) { loadInstanceFonts(); - if (Helper.getUser(this).isPresent()) { + if (Helper.getUser().isPresent()) { getToolbar().onLogin(); } - + Object fragmentToRestore = VaadinSession.getCurrent().getAttribute(SecurityConfiguration.FRAGMENT_TO_RESTORE); if (fragmentToRestore instanceof String) { @@ -260,8 +258,8 @@ protected void init(VaadinRequest request) { } @Override - public ApiClient getClient() { - return new AutoTokenRefreshClient(this, this.getAuthListener()); + public WebClient getWebClient() { + return webClient; } @@ -319,8 +317,4 @@ public ServletContext getServletContext() { public OAuth2ClientProperties getOauth2ClientProperties() { return this.oauth2Clients; } - - public AuthenticationSuccessListener getAuthListener() { - return authListener; - } } diff --git a/src/main/java/org/corpus_tools/annis/gui/AnnisUiApplication.java b/src/main/java/org/corpus_tools/annis/gui/AnnisUiApplication.java index 0d4368cd9e..e6974aec3a 100644 --- a/src/main/java/org/corpus_tools/annis/gui/AnnisUiApplication.java +++ b/src/main/java/org/corpus_tools/annis/gui/AnnisUiApplication.java @@ -7,7 +7,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.PropertySource; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; diff --git a/src/main/java/org/corpus_tools/annis/gui/CommonUI.java b/src/main/java/org/corpus_tools/annis/gui/CommonUI.java index 23fd8c16e8..db19ffe260 100644 --- a/src/main/java/org/corpus_tools/annis/gui/CommonUI.java +++ b/src/main/java/org/corpus_tools/annis/gui/CommonUI.java @@ -21,18 +21,20 @@ import java.util.Map; import java.util.Optional; import javax.servlet.ServletContext; -import org.corpus_tools.annis.ApiClient; -import org.corpus_tools.annis.ApiException; import org.corpus_tools.annis.gui.components.SettingsStorage; +import org.corpus_tools.annis.gui.objects.FontConfig; +import org.corpus_tools.annis.gui.objects.InstanceConfig; import org.corpus_tools.annis.gui.requesthandler.ResourceRequestHandler; -import org.corpus_tools.annis.gui.security.AuthenticationSuccessListener; import org.corpus_tools.annis.gui.security.SecurityConfiguration; +import org.corpus_tools.annis.gui.util.Helper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; +import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * @@ -49,18 +51,14 @@ public abstract class CommonUI extends AnnisBaseUI { private InstanceConfig instanceConfig; - private SecurityContext securityContext; - - protected CommonUI(String urlPrefix, ServiceStarter serviceStarter, - AuthenticationSuccessListener authListener) { - this.urlPrefix = urlPrefix; - Optional desktopAuth = serviceStarter.getDesktopUserToken(); - if (desktopAuth.isPresent()) { - // Login the provided desktop user - getSecurityContext().setAuthentication(desktopAuth.get()); - authListener.setToken(desktopAuth.get().getCredentials().toString()); - } - } + protected CommonUI(String urlPrefix, ServiceStarter serviceStarter) { + this.urlPrefix = urlPrefix; + Optional userToken = serviceStarter.getDesktopUserToken(); + if(userToken.isPresent()) { + // Login with the static desktop token + SecurityContextHolder.getContext().setAuthentication(userToken.get()); + } + } public InstanceConfig getInstanceConfig() { @@ -173,9 +171,7 @@ public String getUrlPrefix() { return urlPrefix; } - public abstract ApiClient getClient(); - - + public abstract WebClient getWebClient(); /** * Handle common errors like database/service connection problems and display a unified error @@ -194,10 +190,10 @@ public boolean handleCommonError(Throwable ex, String action) { rootCause = rootCause.getCause(); } - if (rootCause instanceof ApiException) { - ApiException apiEx = (ApiException) rootCause; + if (rootCause instanceof WebClientResponseException) { + WebClientResponseException apiEx = (WebClientResponseException) rootCause; - if (apiEx.getCode() == 503) { + if (apiEx.getStatusCode() == HttpStatus.SERVICE_UNAVAILABLE) { // database connection error Notification n = new Notification( "Can't execute " + (action == null ? "" : "\"" + action + "\"") @@ -214,7 +210,7 @@ public boolean handleCommonError(Throwable ex, String action) { n.show(this.getPage()); return true; - } else if (apiEx.getCode() == 401) { + } else if (apiEx.getStatusCode() == HttpStatus.UNAUTHORIZED) { redirectToLogin(); return true; } @@ -225,13 +221,6 @@ public boolean handleCommonError(Throwable ex, String action) { public abstract ServletContext getServletContext(); - public SecurityContext getSecurityContext() { - if (this.securityContext == null) { - this.securityContext = SecurityContextHolder.getContext(); - } - return securityContext; - } - public abstract OAuth2ClientProperties getOauth2ClientProperties(); public abstract UIConfig getConfig(); diff --git a/src/main/java/org/corpus_tools/annis/gui/CorpusBrowserPanel.java b/src/main/java/org/corpus_tools/annis/gui/CorpusBrowserPanel.java deleted file mode 100644 index 7e6e613860..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/CorpusBrowserPanel.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright 2011 Corpuslinguistic working group Humboldt University Berlin. - * - * 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.corpus_tools.annis.gui; - -import com.vaadin.event.selection.SelectionEvent; -import com.vaadin.event.selection.SelectionListener; -import com.vaadin.ui.Accordion; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.Label; -import com.vaadin.ui.Panel; -import com.vaadin.ui.ProgressBar; -import com.vaadin.ui.UI; -import com.vaadin.ui.VerticalLayout; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.api.model.AnnoKey; -import org.corpus_tools.annis.api.model.Annotation; -import org.corpus_tools.annis.api.model.AnnotationComponentType; -import org.corpus_tools.annis.api.model.Component; -import org.corpus_tools.annis.gui.beans.CorpusBrowserEntry; -import org.corpus_tools.annis.gui.components.ExceptionDialog; -import org.corpus_tools.annis.gui.objects.Query; -import org.corpus_tools.annis.gui.objects.QueryLanguage; - -/** - * - * @author Thomas Krause {@literal } - */ -public class CorpusBrowserPanel extends Panel { - - private class ExampleListener implements SelectionListener { - - private static final long serialVersionUID = 5456621606184042619L; - - @Override - public void selectionChange(SelectionEvent event) { - Optional selected = event.getFirstSelectedItem(); - Set corpusNameSet = new HashSet<>(); - if (corpus != null) { - corpusNameSet.add(corpus); - } - if (controller != null && selected.isPresent()) { - controller.setQuery(new Query(selected.get().getQuery(), QueryLanguage.AQL, corpusNameSet)); - } - } - } - - private static final long serialVersionUID = -1029743017413951838L; - - private String corpus; - - private ExampleTable tblNodeAnno; - - private Label lblNoNodeAnno; - - private ExampleTable tblEdgeTypes; - - private Label lblNoEdgeTypes; - - private ExampleTable tblEdgeAnno; - - private Label lblNoEdgeAnno; - - private ExampleTable tblMetaAnno; - - private Label lblNoMetaAnno; - - private QueryController controller; - - private ProgressBar progress; - - private Accordion accordion; - - private VerticalLayout layout; - - public CorpusBrowserPanel() { - this(null, null); - } - - public CorpusBrowserPanel(String corpus, QueryController controller) { - super("Available annotations"); - this.corpus = corpus; - this.controller = controller; - - setSizeFull(); - progress = new ProgressBar(); - progress.setIndeterminate(true); - - tblNodeAnno = new ExampleTable(); - tblEdgeTypes = new ExampleTable(); - tblEdgeAnno = new ExampleTable(); - tblMetaAnno = new ExampleTable(); - - tblNodeAnno.addSelectionListener(new ExampleListener()); - tblEdgeTypes.addSelectionListener(new ExampleListener()); - tblEdgeAnno.addSelectionListener(new ExampleListener()); - tblMetaAnno.addSelectionListener(new ExampleListener()); - - tblNodeAnno.sort("name"); - tblEdgeTypes.sort("name"); - tblEdgeAnno.sort("name"); - - lblNoNodeAnno = new Label("(No Node Annotations)"); - VerticalLayout tabNodeAnno = new VerticalLayout(tblNodeAnno, lblNoNodeAnno); - tabNodeAnno.setCaption("Node Annotations"); - tabNodeAnno.setMargin(false); - - lblNoEdgeAnno = new Label("(No Edge Annotations)"); - VerticalLayout tabEdgeAnno = new VerticalLayout(tblEdgeAnno, lblNoEdgeAnno); - tabEdgeAnno.setCaption("Edge Annotations"); - tabEdgeAnno.setMargin(false); - - lblNoEdgeTypes = new Label("(No Edge Types)"); - VerticalLayout tabEdgeTypes = new VerticalLayout(tblEdgeTypes, lblNoEdgeTypes); - tabEdgeTypes.setCaption("Edge Types"); - tabEdgeTypes.setMargin(false); - - lblNoMetaAnno = new Label("(No Meta Annotations)"); - VerticalLayout tabMetaAnno = new VerticalLayout(tblMetaAnno, lblNoMetaAnno); - tabMetaAnno.setCaption("Meta Annotations"); - tabMetaAnno.setMargin(false); - - accordion = new Accordion(tabNodeAnno, tabEdgeAnno, tabEdgeTypes, tabMetaAnno); - accordion.setSizeFull(); - accordion.setVisible(true); - progress.setVisible(true); - progress.setSizeFull(); - - layout = new VerticalLayout(); - layout.addComponents(progress, accordion); - layout.setSizeFull(); - layout.setMargin(false); - layout.setComponentAlignment(progress, Alignment.MIDDLE_CENTER); - setContent(layout); - - } - - @Override - public void attach() { - super.attach(); - - final UI ui = getUI(); - - Background.run(() -> { - fetchAnnotationsInBackground(ui); - }); - } - - private boolean canExcludeNamespace(Collection annos) { - Set names = new HashSet<>(); - for (Annotation a : annos) { - if (!names.add(a.getKey().getName())) { - return false; - } - } - return true; - } - - private void fetchAnnotationsInBackground(UI ui) { - CorporaApi api = new CorporaApi(Helper.getClient(ui)); - - try { - final List nodeAnnos = api.nodeAnnotations(corpus, true, true).stream() - .filter(a -> !Objects.equals(a.getKey().getNs(), "annis") - && !Objects.equals(a.getKey().getName(), "tok")) - .collect(Collectors.toList()); - - final List metaAnnos = new LinkedList<>(nodeAnnos); - - final Set metaAnnoKeys = Helper.getMetaAnnotationNames(corpus, ui); - nodeAnnos.removeIf(anno -> metaAnnoKeys.contains(anno.getKey())); - metaAnnos.removeIf(anno -> !metaAnnoKeys.contains(anno.getKey())); - - final List components = api.components(corpus, "Dominance", null); - final List allEdgeAnnos = new LinkedList<>(); - final Map> edgeAnnosByComponent = new LinkedHashMap<>(); - components.addAll(api.components(corpus, "Pointing", null)); - for (Component c : components) { - try { - List annos = api.edgeAnnotations(corpus, c.getType().getValue(), c.getLayer(), - c.getName(), true, true); - edgeAnnosByComponent.put(c, annos); - allEdgeAnnos.addAll(annos); - } catch (ApiException ex) { - // Ignore any not found errors - } - } - - getUI().access(() -> { - - TreeSet nodeAnnoItems = new TreeSet<>(); - TreeSet edgeAnnoItems = new TreeSet<>(); - TreeSet edgeTypeItems = new TreeSet<>(); - TreeSet metaAnnoItems = new TreeSet<>(); - - progress.setVisible(false); - accordion.setVisible(true); - - boolean stripNodeAnno = canExcludeNamespace(nodeAnnos); - boolean stripEdgeName = canExcludeNamespace(allEdgeAnnos); - boolean stripEdgeAnno = true; - HashSet nodeAnnoNames = new HashSet<>(); - HashSet edgeAnnoNames = new HashSet<>(); - HashSet edgeNames = new HashSet<>(); - boolean hasDominance = false; - boolean hasEmptyDominance = false; - - // do some preparations first - for (Annotation a : nodeAnnos) { - // check for ambiguous names - if (!nodeAnnoNames.add(a.getKey().getName())) { - stripNodeAnno = false; - } - } - for (Component c : components) { - // check if collected edge names are unique - if (!edgeNames.add(Helper.getQName(c))) { - stripEdgeName = false; - } - // check if we need to add the general dominance example edge - if (c.getType() == AnnotationComponentType.DOMINANCE) { - hasDominance = true; - if (c.getName() == null || c.getName().isEmpty()) { - hasEmptyDominance = true; - } - } - } - - for (List annos : edgeAnnosByComponent.values()) { - for (Annotation a : annos) { - // check for ambiguous names - if (!edgeAnnoNames.add(a.getKey().getName())) { - stripEdgeAnno = false; - } - } - } - - // fill the actual containers - for (Annotation a : nodeAnnos) { - String name = stripNodeAnno ? a.getKey().getName() : Helper.getQName(a.getKey()); - CorpusBrowserEntry cbe = new CorpusBrowserEntry(); - cbe.setName(name); - cbe.setExample(name + "=\"" + a.getVal() + "\""); - cbe.setCorpus(corpus); - nodeAnnoItems.add(cbe); - } - - // edge type entry - if (hasDominance && !hasEmptyDominance) { - CorpusBrowserEntry cbe = new CorpusBrowserEntry(); - cbe.setName("(dominance)"); - cbe.setCorpus(corpus); - cbe.setExample("node & node & #1 > #2"); - edgeTypeItems.add(cbe); - } - for (Component c : components) { - CorpusBrowserEntry cbeEdgeType = new CorpusBrowserEntry(); - String name = stripEdgeName ? c.getName() : Helper.getQName(c); - if ((name == null || name.isEmpty()) - && c.getType() == AnnotationComponentType.DOMINANCE) { - cbeEdgeType.setName("(dominance)"); - } else { - cbeEdgeType.setName(name); - } - cbeEdgeType.setCorpus(corpus); - if (c.getType() == AnnotationComponentType.POINTING) { - cbeEdgeType.setExample("node & node & #1 ->" + c.getName() + " #2"); - } else if (c.getType() == AnnotationComponentType.DOMINANCE) { - cbeEdgeType.setExample("node & node & #1 >" + c.getName() + " #2"); - } - edgeTypeItems.add(cbeEdgeType); - } - - // edge annotation entries - for (Map.Entry> entry : edgeAnnosByComponent.entrySet()) { - Component c = entry.getKey(); - for (Annotation a : entry.getValue()) { - CorpusBrowserEntry cbeEdgeAnno = new CorpusBrowserEntry(); - String edgeAnno = stripEdgeAnno ? a.getKey().getName() : Helper.getQName(a.getKey()); - cbeEdgeAnno.setName(edgeAnno); - cbeEdgeAnno.setCorpus(corpus); - if (c.getType() == AnnotationComponentType.POINTING) { - cbeEdgeAnno.setExample("node & node & #1 ->" + c.getName() + "[" - + a.getKey().getName() + "=\"" + a.getVal() + "\"] #2"); - } else if (c.getType() == AnnotationComponentType.DOMINANCE) { - cbeEdgeAnno.setExample( - "node & node & #1 >[" + a.getKey().getName() + "=\"" + a.getVal() + "\"] #2"); - } - edgeAnnoItems.add(cbeEdgeAnno); - - } - } - - boolean stripMetaName = canExcludeNamespace(metaAnnos); - for (Annotation a : nodeAnnos) { - String name = stripMetaName ? a.getKey().getName() : Helper.getQName(a.getKey()); - CorpusBrowserEntry cbe = new CorpusBrowserEntry(); - cbe.setName(name); - cbe.setExample(name + "=\"" + a.getVal() + "\""); - cbe.setCorpus(corpus); - nodeAnnoItems.add(cbe); - } - for (Annotation a : metaAnnos) { - String name = stripNodeAnno ? a.getKey().getName() : Helper.getQName(a.getKey()); - CorpusBrowserEntry cbe = new CorpusBrowserEntry(); - cbe.setName(name); - cbe.setExample(name + "=\"" + a.getVal() + "\""); - cbe.setCorpus(corpus); - metaAnnoItems.add(cbe); - } - - lblNoNodeAnno.setVisible(nodeAnnoItems.isEmpty()); - tblNodeAnno.setVisible(!nodeAnnoItems.isEmpty()); - tblNodeAnno.setItems(new ArrayList<>(nodeAnnoItems)); - - lblNoEdgeAnno.setVisible(edgeAnnoItems.isEmpty()); - tblEdgeAnno.setVisible(!edgeAnnoItems.isEmpty()); - tblEdgeAnno.setItems(edgeAnnoItems); - - lblNoEdgeTypes.setVisible(edgeTypeItems.isEmpty()); - tblEdgeTypes.setVisible(!edgeTypeItems.isEmpty()); - tblEdgeTypes.setItems(edgeTypeItems); - - lblNoMetaAnno.setVisible(metaAnnoItems.isEmpty()); - tblMetaAnno.setVisible(!metaAnnoItems.isEmpty()); - tblMetaAnno.setItems(metaAnnoItems); - - }); - - } catch (ApiException e) { - getUI().access(() -> { - ExceptionDialog.show(e, "Error fetching corpus annotations", getUI()); - }); - } - - } -} diff --git a/src/main/java/org/corpus_tools/annis/gui/EmbeddedVisUI.java b/src/main/java/org/corpus_tools/annis/gui/EmbeddedVisUI.java index 777865fb63..fa2e6578b5 100644 --- a/src/main/java/org/corpus_tools/annis/gui/EmbeddedVisUI.java +++ b/src/main/java/org/corpus_tools/annis/gui/EmbeddedVisUI.java @@ -16,7 +16,6 @@ import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.base.Splitter; -import com.google.common.util.concurrent.FutureCallback; import com.vaadin.annotations.Push; import com.vaadin.annotations.Theme; import com.vaadin.annotations.Widgetset; @@ -31,10 +30,12 @@ import com.vaadin.ui.Label; import com.vaadin.ui.Link; import com.vaadin.ui.Panel; +import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; @@ -46,21 +47,17 @@ import java.util.Optional; import javax.servlet.ServletContext; import javax.xml.stream.XMLStreamException; -import okhttp3.Request; -import okhttp3.Response; import org.apache.commons.io.IOUtils; -import org.corpus_tools.annis.ApiClient; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.api.model.QueryLanguage; import org.corpus_tools.annis.api.model.SubgraphWithContext; import org.corpus_tools.annis.api.model.VisualizerRule; import org.corpus_tools.annis.gui.docbrowser.DocBrowserController; import org.corpus_tools.annis.gui.graphml.DocumentGraphMapper; +import org.corpus_tools.annis.gui.objects.InstanceConfig; import org.corpus_tools.annis.gui.objects.Match; import org.corpus_tools.annis.gui.objects.RawTextWrapper; -import org.corpus_tools.annis.gui.security.AuthenticationSuccessListener; -import org.corpus_tools.annis.gui.security.AutoTokenRefreshClient; import org.corpus_tools.annis.gui.util.ANNISFontIcon; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.annis.gui.util.IDGenerator; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.visualizers.VisualizerPlugin; import org.corpus_tools.annis.gui.visualizers.htmlvis.HTMLVis; @@ -70,10 +67,13 @@ import org.corpus_tools.salt.common.SDocumentGraph; import org.corpus_tools.salt.common.SaltProject; import org.corpus_tools.salt.core.SNode; +import org.reactivestreams.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.web.reactive.function.client.WebClient; /** * @@ -85,46 +85,58 @@ @Widgetset("org.corpus_tools.annis.gui.widgets.gwt.AnnisWidgetSet") public class EmbeddedVisUI extends CommonUI { - private class GraphMLLoaderCallback implements FutureCallback { + private class GraphMLLoaderCallback implements org.reactivestreams.Subscriber { private final String corpusNodeId; private final VisualizerPlugin visPlugin; private final Map args; + private final UI ui; GraphMLLoaderCallback(String corpusNodeId, VisualizerPlugin visPlugin, - Map args) { + Map args, UI ui) { this.visPlugin = visPlugin; this.args = args; this.corpusNodeId = corpusNodeId; + this.ui = ui; } - @Override - public void onFailure(Throwable t) { - log.error("Could not query graph for embedded visualization.", t); - displayMessage("Could not query the result.", t.getMessage()); - } @Override - public void onSuccess(File result) { + public void onNext(File item) { + try { final SaltProject p = SaltFactory.createSaltProject(); SCorpusGraph cg = p.createCorpusGraph(); org.eclipse.emf.common.util.URI docURI = org.eclipse.emf.common.util.URI.createURI("salt:/" + corpusNodeId); SDocument doc = cg.createDocument(docURI); - SDocumentGraph docGraph = DocumentGraphMapper.map(result); - if (Files.deleteIfExists(result.toPath())) { - log.debug("Could not delete temporary SaltXML file {} because it does not exist.", - result.getPath()); - } - + SDocumentGraph docGraph = DocumentGraphMapper.map(item); + Files.deleteIfExists(item.toPath()); doc.setDocumentGraph(docGraph); - - generateVisualizerFromDocument(doc, args, visPlugin); + ui.access(() -> generateVisualizerFromDocument(doc, args, visPlugin)); } catch (IOException | XMLStreamException ex) { log.error("Could not parse GraphML", ex); displayMessage("Could not parse GraphML", ex.toString()); } + + } + + @Override + public void onError(Throwable t) { + log.error("Could not query graph for embedded visualization.", t); + ui.access(() -> displayMessage("Could not query the result.", t.getMessage())); + } + + @Override + public void onComplete() { + // Nothing to do + } + + + @Override + public void onSubscribe(Subscription s) { + // TODO Auto-generated method stub + } } @@ -170,14 +182,13 @@ public void onSuccess(File result) { private transient OAuth2ClientProperties oauth2Clients; @Autowired - private UIConfig config; - - private final AuthenticationSuccessListener authListener; + private WebClient webClient; @Autowired - public EmbeddedVisUI(ServiceStarter serviceStarter, AuthenticationSuccessListener authListener) { - super(URL_PREFIX, serviceStarter, authListener); - this.authListener = authListener; + private UIConfig config; + + public EmbeddedVisUI(ServiceStarter serviceStarter) { + super(URL_PREFIX, serviceStarter); } private void displayGeneralHelp() { @@ -217,12 +228,9 @@ private void generateVisFromParameters(final String visName, Map corpusPathRaw = Helper.getCorpusPath(matchId, false); @@ -231,13 +239,18 @@ private void generateVisFromParameters(final String visName, Map api.subgraphForQuery(toplevelCorpus, aql, QueryLanguage.AQL, null), - new GraphMLLoaderCallback(corpusNodeId, visPlugin.get(), args)); + GraphMLLoaderCallback callback = + new GraphMLLoaderCallback(corpusNodeId, visPlugin.get(), args, UI.getCurrent()); + + webClient.get() + .uri(ub -> ub.path("/corpora/{corpus}/subgraph-for-query").queryParam("query", aql) + .build(toplevelCorpus)) + .retrieve().bodyToMono(new ParameterizedTypeReference() {}).subscribe(callback); + } else { @@ -251,9 +264,10 @@ private void generateVisFromParameters(final String visName, Map api.subgraphForNodes(toplevelCorpus, subgraphQuery), - new GraphMLLoaderCallback(corpusNodeId, visPlugin.get(), args)); + GraphMLLoaderCallback callback = + new GraphMLLoaderCallback(corpusNodeId, visPlugin.get(), args, UI.getCurrent()); + webClient.post().uri("/corpora/{corpus}/subgraph", toplevelCorpus).bodyValue(subgraphQuery) + .retrieve().bodyToMono(new ParameterizedTypeReference() {}).subscribe(callback); } } @@ -358,46 +372,34 @@ private void generateVisFromRemoteSaltURL(final String visName, final String raw if (visPluginOpt.isPresent()) { VisualizerPlugin visPlugin = visPluginOpt.get(); - URI uri = new URI(rawUri); - // fetch content of the URI - ApiClient client = Helper.getClient(this); - displayLoadingIndicator(); // copy the arguments for using them later in the callback final Map argsCopy = new LinkedHashMap<>(args); - Request request = new okhttp3.Request.Builder().url(uri.toASCIIString()).build(); - - Background.runWithCallback(() -> client.getHttpClient().newCall(request).execute(), - new FutureCallback() { - @Override - public void onFailure(Throwable t) { - log.error("Could not query Salt graph for embedded visualization.", t); - displayMessage("Could not query the result.", t.getMessage()); - } - @Override - public void onSuccess(Response response) { - try { - File tmpFile = File.createTempFile("embeddded-vis-fetched-result-", ".salt"); - try (FileOutputStream out = new FileOutputStream(tmpFile)) { - IOUtils.copy(response.body().byteStream(), out); - } catch (IOException ex) { - log.error("Could not copy fetched GraphML file:", ex); - } - - SDocument doc = SaltFactory.createSDocument(); - doc.loadDocumentGraph( - org.eclipse.emf.common.util.URI.createFileURI(tmpFile.getAbsolutePath())); - generateVisualizerFromDocument(doc, argsCopy, visPlugin); + final UI ui = UI.getCurrent(); + URI uri = new URI(rawUri); + webClient.get().uri(uri).retrieve().toEntity(InputStream.class) + .subscribe(body -> ui.access(() -> { + try { + File tmpFile = File.createTempFile("embeddded-vis-fetched-result-", ".salt"); + try (FileOutputStream out = new FileOutputStream(tmpFile)) { + IOUtils.copy(body.getBody(), out); } catch (IOException ex) { - log.error("Could not parse Salt XML", ex); - displayMessage("Could not parse Salt XML", ex.toString()); + log.error("Could not copy fetched GraphML file:", ex); } - } - }); + SDocument doc = SaltFactory.createSDocument(); + doc.loadDocumentGraph( + org.eclipse.emf.common.util.URI.createFileURI(tmpFile.getAbsolutePath())); + generateVisualizerFromDocument(doc, argsCopy, visPlugin); + + } catch (IOException ex) { + log.error("Could not parse Salt XML", ex); + displayMessage("Could not parse Salt XML", ex.toString()); + } + })); } else { displayMessage("Unknown visualizer \"" + visName + "\"", "This ANNIS instance does not know the given visualizer."); @@ -459,8 +461,7 @@ protected void attachToPath(String rawPath, VaadinRequest request) { addStyleName("loaded-embedded-vis"); } - private void showHtmlDoc(String corpus, String documentNodeName, - Map args) { + private void showHtmlDoc(String corpus, String documentNodeName, Map args) { // do nothing for empty fragments if (args == null || args.isEmpty()) { return; @@ -500,13 +501,13 @@ private void showHtmlDoc(String corpus, String documentNodeName, + ""); } } - + + @Override - public ApiClient getClient() { - return new AutoTokenRefreshClient(this, this.authListener); + public WebClient getWebClient() { + return webClient; } - @Override public ServletContext getServletContext() { return servletContext; diff --git a/src/main/java/org/corpus_tools/annis/gui/ExampleQueriesPanel.java b/src/main/java/org/corpus_tools/annis/gui/ExampleQueriesPanel.java deleted file mode 100644 index d3abc17ea6..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/ExampleQueriesPanel.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2013 Corpuslinguistic working group Humboldt University Berlin. - * - * 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.corpus_tools.annis.gui; - -import com.vaadin.icons.VaadinIcons; -import com.vaadin.server.Resource; -import com.vaadin.shared.ui.ContentMode; -import com.vaadin.ui.Button; -import com.vaadin.ui.Component; -import com.vaadin.ui.CssLayout; -import com.vaadin.ui.Grid; -import com.vaadin.ui.Grid.Column; -import com.vaadin.ui.Grid.SelectionMode; -import com.vaadin.ui.Label; -import com.vaadin.ui.ProgressBar; -import com.vaadin.ui.TabSheet; -import com.vaadin.ui.UI; -import com.vaadin.ui.themes.ValoTheme; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.api.model.CorpusConfiguration; -import org.corpus_tools.annis.api.model.ExampleQuery; -import org.corpus_tools.annis.gui.controlpanel.ControlPanel; -import org.corpus_tools.annis.gui.controlpanel.CorpusListPanel; -import org.corpus_tools.annis.gui.controlpanel.QueryPanel; -import org.corpus_tools.annis.gui.objects.Query; -import org.corpus_tools.annis.gui.objects.QueryLanguage; -import org.corpus_tools.annis.gui.resultview.ResultViewPanel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Wraps the auto generated queries. - * - * @author Benjamin Weißenfels {@literal } - */ -public class ExampleQueriesPanel extends CssLayout { - - private class ExampleFetcher implements Runnable { - - private final Set selectedCorpora; - - private UI ui; - - public ExampleFetcher(Set selectedCorpora, UI ui) { - this.selectedCorpora = selectedCorpora; - this.ui = ui; - } - - @Override - public void run() { - final List result = new LinkedList<>(); - try { - result.addAll(loadExamplesFromRemote(selectedCorpora, ui)); - } finally { - ui.access(() -> { - loadingIndicator.setVisible(false); - table.setVisible(true); - - try { - table.setItems(result); - if (result.isEmpty()) { - hideTabSheet(); - } else { - showTab(); - } - } catch (Exception ex) { - log.error("removing or adding of example queries failed for {}", - selectedCorpora, ex); - } - }); - } - - } - - } - - /** - * - */ - private static final long serialVersionUID = -2676130295297213669L; - - // gets the - private final static Logger log = LoggerFactory.getLogger(ExampleQueriesPanel.class); - - private static final Resource SEARCH_ICON = VaadinIcons.SEARCH; - - /** - * Loads the available example queries for a specific corpus. - * - * @param corpusNames Specifies the corpora example queries are fetched for. If it is null or - * empty all available example queries are fetched. - */ - private static List loadExamplesFromRemote(Set corpusNames, UI ui) { - - List result = new LinkedList<>(); - CorporaApi api = new CorporaApi(Helper.getClient(ui)); - try { - if (corpusNames != null && !corpusNames.isEmpty()) { - for (String c : corpusNames) { - CorpusConfiguration config = api.corpusConfiguration(c); - if(config.getExampleQueries() != null) { - for (ExampleQuery q : config.getExampleQueries()) { - Entry e = new Entry(); - e.corpus = c; - e.example = q; - result.add(e); - } - } - } - } - } catch (ApiException ex) { - log.error("problems with getting example queries from remote for {}", corpusNames, ex); - } - return result; - } - - private final String COLUMN_EXAMPLE_QUERY = "exampleQuery"; - - private final String COLUMN_OPEN_CORPUS_BROWSER = "open corpus browser"; - - private final String COLUMN_DESCRIPTION = "description"; - - // main ui window - private final AnnisUI ui; - - private final Grid table; - - private final ProgressBar loadingIndicator; - - - // reference to the tab which holds this component - private TabSheet.Tab tab; - - // hold the parent tab of annis3 - private final HelpPanel parentTab; - - public static class Entry { - String corpus; - ExampleQuery example; - } - - public ExampleQueriesPanel(AnnisUI ui, HelpPanel parentTab) { - super(); - this.ui = ui; - this.parentTab = parentTab; - - loadingIndicator = new ProgressBar(); - loadingIndicator.setIndeterminate(true); - loadingIndicator.setCaption("Loading example queries..."); - loadingIndicator.setVisible(false); - addComponent(loadingIndicator); - - table = new Grid(); - table.setVisible(true); - addComponent(table); - - setUpTable(); - } - - private Component getOpenCorpusPanel(final String corpusName) { - final Button btn = new Button(corpusName); - - btn.setStyleName(ValoTheme.BUTTON_LINK); - btn.addClickListener(event -> { - CorpusListPanel corpusList = ui.getSearchView().getControlPanel().getCorpusList(); - corpusList.initCorpusBrowser(corpusName, btn); - }); - - return btn; - } - - private void hideTabSheet() { - if (parentTab != null) { - tab = parentTab.getTab(this); - - if (tab != null) { - tab.setEnabled(false); - } - } - } - - /** - * Sets the selected corpora and causes a reload - * - * @param selectedCorpora Specifies the corpora example queries are fetched for. If it is null, - * all available example queries are fetched. - */ - public void setSelectedCorpusInBackground(final Set selectedCorpora) { - loadingIndicator.setVisible(true); - table.setVisible(false); - Background.run(new ExampleFetcher(selectedCorpora, UI.getCurrent())); - } - - /** - * Sets some layout properties. - */ - private void setUpTable() { - setSizeFull(); - // expand the table - table.setSizeFull(); - - // Allow selecting items from the table. - table.setSelectionMode(SelectionMode.SINGLE); - - // set custom style - table.addStyleName("example-queries-table"); - - // configure columns - Column corpusBrowserColumn = table.addComponentColumn(e -> { - return getOpenCorpusPanel(e.corpus); - }); - corpusBrowserColumn.setId(COLUMN_OPEN_CORPUS_BROWSER); - corpusBrowserColumn.setCaption("open corpus browser"); - - Column exampleQueryColumn = table.addComponentColumn(e -> { - Button btn = new Button(); - btn.setDescription("show corpus browser for " + e.corpus); - btn.addStyleName(ValoTheme.BUTTON_LINK); - btn.setIcon(SEARCH_ICON); - btn.setCaption(e.example.getQuery()); - btn.setDescription("show results for \"" + e.example.getQuery() + "\" in " + e.corpus); - btn.addStyleName(Helper.CORPUS_FONT_FORCE); - - btn.addClickListener(event -> { - if (ui != null) { - ControlPanel controlPanel = ui.getSearchView().getControlPanel(); - QueryPanel queryPanel; - - if (controlPanel == null) { - log.error("controlPanel is not initialized"); - return; - } - - queryPanel = controlPanel.getQueryPanel(); - if (queryPanel == null) { - log.error("queryPanel is not initialized"); - return; - } - - Set corpusNameSet = new HashSet<>(); - corpusNameSet.add(e.corpus); - if (ui.getQueryController() != null) { - QueryLanguage ql = QueryLanguage.AQL; - if (e.example - .getQueryLanguage() == org.corpus_tools.annis.api.model.QueryLanguage.AQLQUIRKSV3) { - ql = QueryLanguage.AQL_QUIRKS_V3; - } - ui.getQueryController() - .setQuery(new Query(e.example.getQuery(), ql, corpusNameSet)); - // execute query - ui.getQueryController().executeSearch(true, true); - } - } - }); - return btn; - }); - exampleQueryColumn.setId(COLUMN_EXAMPLE_QUERY); - exampleQueryColumn.setCaption("Example Query"); - - Column descriptionColumn = table.addComponentColumn(e -> { - Label l = new Label(e.example.getDescription()); - l.setContentMode(ContentMode.TEXT); - l.addStyleName(Helper.CORPUS_FONT_FORCE); - return l; - }); - descriptionColumn.setCaption("Description"); - descriptionColumn.setId(COLUMN_DESCRIPTION); - - table.setColumns(COLUMN_EXAMPLE_QUERY, COLUMN_DESCRIPTION, COLUMN_OPEN_CORPUS_BROWSER); - - exampleQueryColumn.setExpandRatio(1); - descriptionColumn.setExpandRatio(1); - - } - - /** - * Shows the tab and put into the foreground, if no query is executed yet. - */ - private void showTab() { - if (parentTab != null) { - tab = parentTab.getTab(this); - if (tab != null) { - // FIXME: this should be added by the constructor or by the panel that adds this - // tab - // tab.getComponent().addStyleName("example-queries-tab"); - tab.setEnabled(true); - - if (!(parentTab.getSelectedTab() instanceof ResultViewPanel)) { - parentTab.setSelectedTab(tab); - } - } - } - - } -} diff --git a/src/main/java/org/corpus_tools/annis/gui/LoginDataLostException.java b/src/main/java/org/corpus_tools/annis/gui/LoginDataLostException.java deleted file mode 100644 index fc490ffb62..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/LoginDataLostException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2015 Corpuslinguistic working group Humboldt University Berlin. - * - * 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.corpus_tools.annis.gui; - -/** - * This indicates the login-data is not available any longer. - * - * If this exception was thrown the user interface should show to the user that he/she is effectivly - * logged out. - * - * @author Thomas Krause {@literal } - */ -public class LoginDataLostException extends Exception { - - /** - * - */ - private static final long serialVersionUID = -4957745243871859779L; - -} diff --git a/src/main/java/org/corpus_tools/annis/gui/MetaDataPanel.java b/src/main/java/org/corpus_tools/annis/gui/MetaDataPanel.java deleted file mode 100644 index cd8d225f70..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/MetaDataPanel.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright 2011 Corpuslinguistic working group Humboldt University Berlin. - * - * 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.corpus_tools.annis.gui; - -import com.google.common.collect.ComparisonChain; -import com.google.common.util.concurrent.FutureCallback; -import com.vaadin.data.ValueProvider; -import com.vaadin.data.provider.ListDataProvider; -import com.vaadin.server.SerializableComparator; -import com.vaadin.shared.data.sort.SortDirection; -import com.vaadin.shared.ui.ContentMode; -import com.vaadin.ui.Accordion; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.Grid; -import com.vaadin.ui.Grid.Column; -import com.vaadin.ui.Label; -import com.vaadin.ui.Panel; -import com.vaadin.ui.ProgressBar; -import com.vaadin.ui.UI; -import com.vaadin.ui.VerticalLayout; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import org.corpus_tools.annis.api.model.AnnoKey; -import org.corpus_tools.annis.api.model.Annotation; -import org.corpus_tools.annis.api.model.CorpusConfiguration; -import org.corpus_tools.annis.gui.components.ExceptionDialog; -import org.corpus_tools.salt.common.SCorpus; -import org.corpus_tools.salt.common.SCorpusGraph; -import org.corpus_tools.salt.common.SDocument; -import org.corpus_tools.salt.core.SMetaAnnotation; -import org.eclipse.emf.common.util.URI; - -/** - * Provides all corpus annotations for a corpus or for a specific search result. - * - * // TODO cleanup the toplevelCorpus side effects. - * - * @author Thomas Krause {@literal } - * @author Benjamin Weißenfels {@literal } - */ -public class MetaDataPanel extends Panel { - - private static class CorpusMetadataCallResult { - SCorpusGraph metadata; - CorpusConfiguration config; - } - - private static class ConfiguredSortOrderComparator implements SerializableComparator { - - private static final long serialVersionUID = 1L; - private ArrayList corpusAnnotationOrder; - - public ConfiguredSortOrderComparator(Collection corpusAnnotationOrder) { - if (corpusAnnotationOrder == null) { - this.corpusAnnotationOrder = new ArrayList<>(); - } else { - this.corpusAnnotationOrder = new ArrayList<>(corpusAnnotationOrder); - } - - } - - @Override - public int compare(Annotation o1, Annotation o2) { - - String q1 = Helper.getQName(o1.getKey()); - q1 = q1 == null ? "" : q1; - String q2 = Helper.getQName(o2.getKey()); - q2 = q2 == null ? "" : q2; - - - int pos1 = this.corpusAnnotationOrder.indexOf(q1); - int pos2 = this.corpusAnnotationOrder.indexOf(q2); - - if (pos1 < 0) { - pos1 = this.corpusAnnotationOrder.size(); - } - if (pos2 < 0) { - pos2 = this.corpusAnnotationOrder.size(); - } - return ComparisonChain.start().compare(pos1, pos2).compare(q1, q2).result(); - } - } - - private final class MetadataAvailableCallback - implements FutureCallback { - @Override - public void onFailure(Throwable t) { - layout.removeComponent(progress); - ExceptionDialog.show(t, "Could not get meta data", getUI()); - } - - @Override - public void onSuccess(CorpusMetadataCallResult result) { - layout.removeComponent(progress); - Accordion accordion = new Accordion(); - accordion.setSizeFull(); - - - boolean hasDocument = addDocumentMetadata(result, accordion); - boolean hasCorpus = addCorpusMetadata(result, accordion); - - // set output to none if no metadata are available - if (hasDocument || hasCorpus) { - layout.addComponent(accordion); - } else { - addEmptyLabel(); - } - } - - private Grid setupTable(ListDataProvider metaData, - CorpusConfiguration config) { - ValueProvider nameProvider = anno -> Helper.getQName(anno.getKey()); - - - if (config == null) { - metaData.setSortOrder(nameProvider, SortDirection.ASCENDING); - } else { - metaData.setSortComparator( - new ConfiguredSortOrderComparator(config.getView().getCorpusAnnotationOrder())); - } - Grid tblMeta = new Grid<>(Annotation.class); - tblMeta.setDataProvider(metaData); - Column nameColumn = tblMeta.addColumn(nameProvider); - nameColumn.setWidthUndefined(); - nameColumn.setCaption("Name"); - nameColumn.setId("genname"); - Column valueColumn = - tblMeta.addComponentColumn(anno -> new Label(anno.getVal(), ContentMode.HTML)); - valueColumn.setId("genval"); - valueColumn.setCaption("Value"); - - tblMeta.setColumns(nameColumn.getId(), valueColumn.getId()); - - tblMeta.setSizeFull(); - valueColumn.setExpandRatio(1); - return tblMeta; - } - - private boolean addCorpusMetadata(CorpusMetadataCallResult result, Accordion accordion) { - boolean hasResult = false; - - // Sort the (sub-) corpora so sub-corpora come first - List corpora = new ArrayList<>(result.metadata.getCorpora()); - corpora.sort((c1, c2) -> { - URI u1 = c1.getPath(); - URI u2 = c2.getPath(); - return ComparisonChain.start().compare(u1.segmentCount(), u2.segmentCount()) - .compare(u1.toString(), u2.toString()).result(); - }); - - for (SCorpus c : corpora) { - List corpusAnnos = new ArrayList<>(); - for (SMetaAnnotation metaAnno : c.getMetaAnnotations()) { - Annotation anno = new Annotation(); - AnnoKey key = new AnnoKey(); - key.setNs(metaAnno.getNamespace()); - key.setName(metaAnno.getName()); - anno.setKey(key); - anno.setVal(metaAnno.getValue_STEXT()); - corpusAnnos.add(anno); - } - - - if (!corpusAnnos.isEmpty()) { - - String path = c.getPath().toString(); - if (path.startsWith("salt:/")) { - path = path.substring("salt:/".length()); - } - path = path + " (corpus)"; - accordion.addTab(setupTable(new ListDataProvider<>(corpusAnnos), result.config), path); - hasResult = true; - } - } - return hasResult; - } - - private boolean addDocumentMetadata(CorpusMetadataCallResult result, Accordion accordion) { - boolean hasResult = false; - - // Add all document metadata first, then the corpus metadata - List documents = result.metadata.getDocuments(); - if (documents != null) { - // There should only be one document in the corpus graph, but keeping the code generic - // should not hurt - for (SDocument d : documents) { - List docAnnos = new ArrayList<>(); - for (SMetaAnnotation metaAnno : d.getMetaAnnotations()) { - Annotation anno = new Annotation(); - AnnoKey key = new AnnoKey(); - key.setNs(metaAnno.getNamespace()); - key.setName(metaAnno.getName()); - anno.setKey(key); - anno.setVal(metaAnno.getValue_STEXT()); - docAnnos.add(anno); - } - - if (!docAnnos.isEmpty()) { - String path = d.getName(); - - // In case we are called to only output a corpus, this might have been mapped as - // document. So only add the "document" suffix in case we are sure it is an actual - // corpus. - if (documentName.isPresent()) { - path = path + " (document)"; - } - - accordion.addTab(setupTable(new ListDataProvider<>(docAnnos), result.config), path); - hasResult = true; - } - } - } - return hasResult; - } - - /** - * Places a label in the middle center of the corpus browser panel. - */ - private void addEmptyLabel() { - if (emptyLabel == null) { - emptyLabel = new Label("none"); - } - - if (corpusAnnotationTable != null) { - layout.removeComponent(corpusAnnotationTable); - } - - layout.addComponent(emptyLabel); - - // this has only an effect after adding the component to a parent. Bug by - // vaadin? - emptyLabel.setSizeUndefined(); - - layout.setComponentAlignment(emptyLabel, Alignment.MIDDLE_CENTER); - layout.setExpandRatio(emptyLabel, 1.0f); - } - } - - /** - * - */ - private static final long serialVersionUID = -3607697674053863447L; - - - private VerticalLayout layout; - - private String toplevelCorpusName; - - // this is only set if the metadata panel is called from a specific result. - private Optional documentName; - - // holds the current corpus annotation table, when called from corpus browser - private Grid corpusAnnotationTable = null; - - private final ProgressBar progress = new ProgressBar(); - - /** - * this empty label is currently use for empty metadata list on the left side of the corpusbrowser - */ - private Label emptyLabel = new Label("(no metadata)"); - - public MetaDataPanel(String toplevelCorpusName) { - this(toplevelCorpusName, Optional.empty()); - } - - public MetaDataPanel(String toplevelCorpusName, Optional documentName) { - super("Metadata"); - - this.toplevelCorpusName = toplevelCorpusName; - this.documentName = documentName; - - setSizeFull(); - layout = new VerticalLayout(); - layout.setSizeFull(); - setContent(layout); - - progress.setIndeterminate(true); - progress.setSizeFull(); - - layout.addComponent(progress); - layout.setComponentAlignment(progress, Alignment.MIDDLE_CENTER); - } - - - @Override - public void attach() { - super.attach(); - - final UI ui = getUI(); - - Background.runWithCallback(() -> { - CorpusMetadataCallResult result = new CorpusMetadataCallResult(); - - result.metadata = Helper.getMetaData(toplevelCorpusName, documentName, ui); - if (ui instanceof AnnisUI) { - result.config = ((AnnisUI) ui).getCorpusConfigWithCache(toplevelCorpusName); - } else { - result.config = Helper.getCorpusConfig(toplevelCorpusName, ui); - } - return result; - - }, new MetadataAvailableCallback()); - } - - -} diff --git a/src/main/java/org/corpus_tools/annis/gui/SearchView.java b/src/main/java/org/corpus_tools/annis/gui/SearchView.java index 5344613e9e..f47f76894a 100644 --- a/src/main/java/org/corpus_tools/annis/gui/SearchView.java +++ b/src/main/java/org/corpus_tools/annis/gui/SearchView.java @@ -26,6 +26,7 @@ import com.vaadin.server.VaadinResponse; import com.vaadin.server.VaadinSession; import com.vaadin.server.WebBrowser; +import com.vaadin.spring.annotation.SpringView; import com.vaadin.ui.Component; import com.vaadin.ui.GridLayout; import com.vaadin.ui.Layout; @@ -46,9 +47,9 @@ import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.gui.components.ExceptionDialog; +import org.corpus_tools.annis.gui.components.HelpPanel; +import org.corpus_tools.annis.gui.components.MainToolbar; import org.corpus_tools.annis.gui.controlpanel.ControlPanel; import org.corpus_tools.annis.gui.docbrowser.DocBrowserController; import org.corpus_tools.annis.gui.frequency.FrequencyQueryPanel; @@ -58,541 +59,538 @@ import org.corpus_tools.annis.gui.media.PDFController; import org.corpus_tools.annis.gui.media.PDFControllerImpl; import org.corpus_tools.annis.gui.objects.DisplayedResultQuery; +import org.corpus_tools.annis.gui.objects.InstanceConfig; import org.corpus_tools.annis.gui.objects.PagedResultQuery; import org.corpus_tools.annis.gui.objects.Query; +import org.corpus_tools.annis.gui.objects.QueryGenerator; import org.corpus_tools.annis.gui.objects.QueryLanguage; +import org.corpus_tools.annis.gui.objects.SidebarState; import org.corpus_tools.annis.gui.resultview.ResultViewPanel; +import org.corpus_tools.annis.gui.security.LoginListener; +import org.corpus_tools.annis.gui.util.Helper; import org.slf4j.LoggerFactory; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * The view which shows the search interface. * * @author Thomas Krause {@literal } */ +@SpringView public class SearchView extends GridLayout - implements View, MimeTypeErrorListener, Page.UriFragmentChangedListener, - TabSheet.CloseHandler, LoginListener, Sidebar, TabSheet.SelectedTabChangeListener { + implements View, MimeTypeErrorListener, Page.UriFragmentChangedListener, TabSheet.CloseHandler, + LoginListener, TabSheet.SelectedTabChangeListener { - private class CitationRequestHandler implements RequestHandler { - - /** - * - */ - private static final long serialVersionUID = -5700172209282345278L; - - @Override - public boolean handleRequest(VaadinSession session, VaadinRequest request, - VaadinResponse response) throws IOException { - checkCitation(); - return false; - } - } + private class CitationRequestHandler implements RequestHandler { /** * */ - private static final long serialVersionUID = 4726183716126582129L; + private static final long serialVersionUID = -5700172209282345278L; - private static final org.slf4j.Logger log = LoggerFactory.getLogger(SearchView.class); + @Override + public boolean handleRequest(VaadinSession session, VaadinRequest request, + VaadinResponse response) throws IOException { + checkCitation(); + return false; + } + } - public static final String NAME = ""; + /** + * + */ + private static final long serialVersionUID = 4726183716126582129L; - private final static Escaper urlPathEscape = UrlEscapers.urlPathSegmentEscaper(); + private static final org.slf4j.Logger log = LoggerFactory.getLogger(SearchView.class); - public final static int CONTROL_PANEL_WIDTH = 400; + public static final String NAME = ""; - // regular expression matching, CLEFT and CRIGHT are optional - // indexes: AQL=1, CIDS=2, CLEFT=4, CRIGHT=6 - private final Pattern citationPattern = Pattern.compile( - "AQL\\((.*)\\),CIDS\\(([^)]*)\\)(,CLEFT\\(([^)]*)\\),)?(CRIGHT\\(([^)]*)\\))?", - Pattern.MULTILINE | Pattern.DOTALL); + private final static Escaper urlPathEscape = UrlEscapers.urlPathSegmentEscaper(); - private ControlPanel controlPanel; + public final static int CONTROL_PANEL_WIDTH = 400; - private TabSheet mainTab; + // regular expression matching, CLEFT and CRIGHT are optional + // indexes: AQL=1, CIDS=2, CLEFT=4, CRIGHT=6 + private final Pattern citationPattern = Pattern.compile( + "AQL\\((.*)\\),CIDS\\(([^)]*)\\)(,CLEFT\\(([^)]*)\\),)?(CRIGHT\\(([^)]*)\\))?", + Pattern.MULTILINE | Pattern.DOTALL); - private String lastEvaluatedFragment; + private ControlPanel controlPanel; - private DocBrowserController docBrowserController; + private TabSheet mainTab; - private Set selectedTabHistory; + private String lastEvaluatedFragment; - private final AnnisUI ui; + private DocBrowserController docBrowserController; - private MainToolbar toolbar; + private Set selectedTabHistory; - private HelpPanel helpPanel; + private final AnnisUI ui; - public SearchView(AnnisUI ui) { - super(2, 2); - this.ui = ui; - this.selectedTabHistory = new LinkedHashSet<>(); + private MainToolbar toolbar; - // init a doc browser controller - this.docBrowserController = new DocBrowserController(ui); + private HelpPanel helpPanel; - setSizeFull(); - setMargin(false); - setRowExpandRatio(1, 1.0f); - setColumnExpandRatio(1, 1.0f); + public SearchView(AnnisUI ui) { + super(2, 2); + this.ui = ui; + this.selectedTabHistory = new LinkedHashSet<>(); - helpPanel = new HelpPanel(ui); + // init a doc browser controller + this.docBrowserController = new DocBrowserController(ui); - mainTab = new TabSheet(); - mainTab.setSizeFull(); - mainTab.setCloseHandler(SearchView.this); - mainTab.addStyleName(ValoTheme.TABSHEET_FRAMED); - mainTab.addSelectedTabChangeListener(SearchView.this); + setSizeFull(); + setMargin(false); + setRowExpandRatio(1, 1.0f); + setColumnExpandRatio(1, 1.0f); - TabSheet.Tab helpTab = mainTab.addTab(helpPanel, "Help/Examples"); - helpTab.setIcon(FontAwesome.QUESTION_CIRCLE); - helpTab.setClosable(false); + helpPanel = new HelpPanel(ui); - controlPanel = new ControlPanel(ui); + mainTab = new TabSheet(); + mainTab.setSizeFull(); + mainTab.setCloseHandler(SearchView.this); + mainTab.addStyleName(ValoTheme.TABSHEET_FRAMED); + mainTab.addSelectedTabChangeListener(SearchView.this); - controlPanel.setWidth(CONTROL_PANEL_WIDTH, Layout.Unit.PIXELS); - controlPanel.setHeight(100f, Layout.Unit.PERCENTAGE); + TabSheet.Tab helpTab = mainTab.addTab(helpPanel, "Help/Examples"); + helpTab.setIcon(FontAwesome.QUESTION_CIRCLE); + helpTab.setClosable(false); - ui.addAction(new ShortcutListener("Tutor^eial") { - /** - * - */ - private static final long serialVersionUID = 4230090114473234656L; + controlPanel = new ControlPanel(ui); - @Override - public void handleAction(Object sender, Object target) { - mainTab.setSelectedTab(helpPanel); - } - }); + controlPanel.setWidth(CONTROL_PANEL_WIDTH, Layout.Unit.PIXELS); + controlPanel.setHeight(100f, Layout.Unit.PERCENTAGE); - addComponent(controlPanel, 0, 1); - addComponent(mainTab, 1, 1); - } + ui.addAction(new ShortcutListener("Tutor^eial") { + /** + * + */ + private static final long serialVersionUID = 4230090114473234656L; - public void checkCitation() { - if (VaadinSession.getCurrent() == null || VaadinSession.getCurrent().getSession() == null) { - return; - } - Object origURLRaw = VaadinSession.getCurrent().getSession().getAttribute("citation"); - if (origURLRaw == null || !(origURLRaw instanceof String)) { - return; - } - String origURL = (String) origURLRaw; - String parameters = origURL.replaceAll(".*?/Cite(/)?", ""); - if (!"".equals(parameters) && !origURL.equals(parameters)) { - try { - String decoded = URLDecoder.decode(parameters, "UTF-8"); - evaluateCitation(decoded); - } catch (UnsupportedEncodingException ex) { - log.error(null, ex); - } - } + @Override + public void handleAction(Object sender, Object target) { + mainTab.setSelectedTab(helpPanel); + } + }); - } + addComponent(controlPanel, 0, 1); + addComponent(mainTab, 1, 1); + } - public void closeTab(Component c) { - selectedTabHistory.remove(c); - mainTab.removeComponent(c); + public void checkCitation() { + if (VaadinSession.getCurrent() == null || VaadinSession.getCurrent().getSession() == null) { + return; + } + Object origURLRaw = VaadinSession.getCurrent().getSession().getAttribute("citation"); + if (origURLRaw == null || !(origURLRaw instanceof String)) { + return; + } + String origURL = (String) origURLRaw; + String parameters = origURL.replaceAll(".*?/Cite(/)?", ""); + if (!"".equals(parameters) && !origURL.equals(parameters)) { + try { + String decoded = URLDecoder.decode(parameters, "UTF-8"); + evaluateCitation(decoded); + } catch (UnsupportedEncodingException ex) { + log.error(null, ex); + } } - @Override - public void enter(ViewChangeListener.ViewChangeEvent event) { - if (event.getOldView() == event.getNewView()) { - return; - } + } - InstanceConfig config = ui.getInstanceConfig(); + public void closeTab(Component c) { + selectedTabHistory.remove(c); + mainTab.removeComponent(c); + } - Page.getCurrent().setTitle(config.getInstanceDisplayName() + " (ANNIS Corpus Search)"); + @Override + public void enter(ViewChangeListener.ViewChangeEvent event) { + if (event.getOldView() == event.getNewView()) { + return; + } - Page.getCurrent().addUriFragmentChangedListener(this); + InstanceConfig config = ui.getInstanceConfig(); - getSession().addRequestHandler(new CitationRequestHandler()); + Page.getCurrent().setTitle(config.getInstanceDisplayName() + " (ANNIS Corpus Search)"); - getSession().setAttribute(MediaController.class, new MediaControllerImpl()); + Page.getCurrent().addUriFragmentChangedListener(this); - getSession().setAttribute(PDFController.class, new PDFControllerImpl()); + getSession().addRequestHandler(new CitationRequestHandler()); - checkCitation(); - lastEvaluatedFragment = ""; - evaluateFragment(Page.getCurrent().getUriFragment()); + getSession().setAttribute(MediaController.class, new MediaControllerImpl()); - if (config.isLoginOnStart() && toolbar != null && !Helper.getUser(ui.getSecurityContext()).isPresent()) { - toolbar.showLoginWindow(); - } + getSession().setAttribute(PDFController.class, new PDFControllerImpl()); - } + checkCitation(); + lastEvaluatedFragment = ""; + evaluateFragment(Page.getCurrent().getUriFragment()); - public void evaluateCitation(String relativeUri) { - Matcher m = citationPattern.matcher(relativeUri); - if (m.matches()) { - // AQL - String aql = ""; - if (m.group(1) != null) { - aql = m.group(1); - } + if (config.isLoginOnStart() && toolbar != null && !Helper.getUser().isPresent()) { + toolbar.showLoginWindow(); + } - // CIDS - Set selectedCorpora = new HashSet<>(); - if (m.group(2) != null) { - String[] cids = m.group(2).split(","); - selectedCorpora.addAll(Arrays.asList(cids)); - } + } + + public void evaluateCitation(String relativeUri) { + Matcher m = citationPattern.matcher(relativeUri); + if (m.matches()) { + // AQL + String aql = ""; + if (m.group(1) != null) { + aql = m.group(1); + } + + // CIDS + Set selectedCorpora = new HashSet<>(); + if (m.group(2) != null) { + String[] cids = m.group(2).split(","); + selectedCorpora.addAll(Arrays.asList(cids)); + } + + // filter by actually available user corpora in order not to get any exception + // later + try { + List userCorpora = ui.getWebClient().get().uri("/corpora").retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}).block(); + selectedCorpora.retainAll(userCorpora); + } catch (WebClientResponseException ex) { + log.error("Could not get list of corpora", ex); + } + + // CLEFT and CRIGHT + if (m.group(4) != null && m.group(6) != null) { + int cleft = 0; + int cright = 0; + try { + cleft = Integer.parseInt(m.group(4)); + cright = Integer.parseInt(m.group(6)); + } catch (NumberFormatException ex) { + log.error("could not parse context value", ex); + } + ui.getQueryController() + .setQuery(new PagedResultQuery(cleft, cright, 0, 10, null, aql, selectedCorpora)); + } else { + ui.getQueryController() + .setQuery(new Query(aql, QueryLanguage.AQL_QUIRKS_V3, selectedCorpora)); + } + + // remove all currently openend sub-windows + Set all = new HashSet<>(ui.getWindows()); + for (Window w : all) { + ui.removeWindow(w); + } + } else { + Notification.show("Invalid citation", Notification.Type.WARNING_MESSAGE); + } - // filter by actually avaible user corpora in order not to get any exception - // later - CorporaApi api = new CorporaApi(Helper.getClient(ui)); - try { - List userCorpora = api.listCorpora(); - selectedCorpora.retainAll(userCorpora); - } catch (ApiException ex) { - log.error("Could not get list of corpora", ex); - } + } - // CLEFT and CRIGHT - if (m.group(4) != null && m.group(6) != null) { - int cleft = 0; - int cright = 0; - try { - cleft = Integer.parseInt(m.group(4)); - cright = Integer.parseInt(m.group(6)); - } catch (NumberFormatException ex) { - log.error("could not parse context value", ex); - } - ui.getQueryController().setQuery( - new PagedResultQuery(cleft, cright, 0, 10, null, aql, selectedCorpora)); - } else { - ui.getQueryController() - .setQuery(new Query(aql, QueryLanguage.AQL_QUIRKS_V3, selectedCorpora)); - } + private void evaluateFragment(String fragment) { + // do nothing if not changed + if (fragment == null || fragment.isEmpty() || fragment.equals(lastEvaluatedFragment)) { + return; + } - // remove all currently openend sub-windows - Set all = new HashSet<>(ui.getWindows()); - for (Window w : all) { - ui.removeWindow(w); - } + Map args = Helper.parseFragment(fragment); + + if (args.containsKey("c")) { + String[] originalCorpusNames = args.get("c").split("\\s*,\\s*"); + Set corpora = new TreeSet(Arrays.asList(originalCorpusNames)); + // Remove all corpora we don't have the access right to + try { + List availableCorporaList = ui.getWebClient().get().uri("/corpora").retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}).block(); + HashSet availableCorpora = new HashSet<>(availableCorporaList); + corpora.removeIf(c -> !availableCorpora.contains(c)); + } catch (WebClientResponseException e) { + ExceptionDialog.show(e, "Could not get corpus list", ui); + } + + if (corpora.isEmpty()) { + if (!Helper.getUser().isPresent() && toolbar != null) { + // not logged in, show login window + toolbar.showLoginWindow(); } else { - Notification.show("Invalid citation", Notification.Type.WARNING_MESSAGE); - } + // already logged in or no login system available, just display a message + new Notification("Linked corpus does not exist", + "

The corpus you wanted to access unfortunally does not (yet) exist" + + " in ANNIS.

" + "

possible reasons are:

" + "
    " + + "
  • that it has not been imported yet,
  • " + + "
  • you don't have the access rights to see this corpus,
  • " + + "
  • or the ANNIS service is not running.
  • " + "
" + + "

Please ask the responsible person of the site that contained " + + "the link to import the corpus.

", + Notification.Type.WARNING_MESSAGE, true).show(Page.getCurrent()); - } - - private void evaluateFragment(String fragment) { - // do nothing if not changed - if (fragment == null || fragment.isEmpty() || fragment.equals(lastEvaluatedFragment)) { - return; } + } // end if corpus list returned from service is empty + else { + if (args.containsKey("c") && args.size() == 1) { + // special case: we were called from outside and should only select, + // but not query, the selected corpora + Query q = new Query(); + q.setCorpora(corpora); + q.setQuery(""); + ui.getQueryController().setQuery(q); + } else if (args.get("cl") != null && args.get("cr") != null) { + // make sure the properties are not overwritten by the background process + getControlPanel().getSearchOptions().setUpdateStateFromConfig(false); + + DisplayedResultQuery query = + QueryGenerator.displayed().left(Integer.parseInt(args.get("cl"))) + .right(Integer.parseInt(args.get("cr"))).offset(Integer.parseInt(args.get("s"))) + .limit(Integer.parseInt(args.get("l"))).segmentation(args.get("seg")) + .baseText(args.get("bt")).query(args.get("q")).corpora(corpora).build(); + + if (query.getBaseText() == null && query.getSegmentation() != null) { + // if no explicit visible segmentation was given use the same as the context + query.setBaseText(query.getSegmentation()); + } + if (query.getBaseText() != null && query.getBaseText().isEmpty()) { + // empty string means "null" + query.setBaseText(null); + } + + String matchSelectionRaw = args.get("m"); + if (matchSelectionRaw != null) { + for (String selectedMatchNr : Splitter.on(',').omitEmptyStrings().trimResults() + .split(matchSelectionRaw)) { + try { + long nr = Long.parseLong(selectedMatchNr); + query.getSelectedMatches().add(nr); + } catch (NumberFormatException ex) { + log.warn("Invalid long provided as selected match", ex); + } + } + } - Map args = Helper.parseFragment(fragment); - - if (args.containsKey("c")) { - String[] originalCorpusNames = args.get("c").split("\\s*,\\s*"); - Set corpora = new TreeSet(Arrays.asList(originalCorpusNames)); - // Remove all corpora we don't have the access right to + if (args.get("o") != null) { try { - CorporaApi api = new CorporaApi(Helper.getClient(ui)); - Set availableCorpora = new HashSet<>(api.listCorpora()); - corpora.removeIf(c -> !availableCorpora.contains(c)); - } catch (ApiException e) { - ExceptionDialog.show(e, "Could not get corpus list", ui); + query.setOrder(DisplayedResultQuery.parseOrderFromCitationFragment(args.get("0"))); + } catch (IllegalArgumentException ex) { + log.warn("Could not parse query fragment argument for order", ex); } + } - if (corpora.isEmpty()) { - if (!Helper.getUser(ui.getSecurityContext()).isPresent() && toolbar != null) { - // not logged in, show login window - toolbar.showLoginWindow(); - } else { - // already logged in or no login system available, just display a message - new Notification("Linked corpus does not exist", - "

The corpus you wanted to access unfortunally does not (yet) exist" - + " in ANNIS.

" + "

possible reasons are:

" + "
    " - + "
  • that it has not been imported yet,
  • " - + "
  • you don't have the access rights to see this corpus,
  • " - + "
  • or the ANNIS service is not running.
  • " + "
" - + "

Please ask the responsible person of the site that contained " - + "the link to import the corpus.

", - Notification.Type.WARNING_MESSAGE, true).show(Page.getCurrent()); - - } - } // end if corpus list returned from service is empty - else { - if (args.containsKey("c") && args.size() == 1) { - // special case: we were called from outside and should only select, - // but not query, the selected corpora - Query q = new Query(); - q.setCorpora(corpora); - q.setQuery(""); - ui.getQueryController().setQuery(q); - } else if (args.get("cl") != null && args.get("cr") != null) { - // make sure the properties are not overwritten by the background process - getControlPanel().getSearchOptions().setUpdateStateFromConfig(false); - - DisplayedResultQuery query = QueryGenerator.displayed() - .left(Integer.parseInt(args.get("cl"))) - .right(Integer.parseInt(args.get("cr"))) - .offset(Integer.parseInt(args.get("s"))) - .limit(Integer.parseInt(args.get("l"))).segmentation(args.get("seg")) - .baseText(args.get("bt")).query(args.get("q")).corpora(corpora).build(); - - if (query.getBaseText() == null && query.getSegmentation() != null) { - // if no explicit visible segmentation was given use the same as the context - query.setBaseText(query.getSegmentation()); - } - if (query.getBaseText() != null && query.getBaseText().isEmpty()) { - // empty string means "null" - query.setBaseText(null); - } - - String matchSelectionRaw = args.get("m"); - if (matchSelectionRaw != null) { - for (String selectedMatchNr : Splitter.on(',').omitEmptyStrings() - .trimResults().split(matchSelectionRaw)) { - try { - long nr = Long.parseLong(selectedMatchNr); - query.getSelectedMatches().add(nr); - } catch (NumberFormatException ex) { - log.warn("Invalid long provided as selected match", ex); - } - } - } - - if (args.get("o") != null) { - try { - query.setOrder( - DisplayedResultQuery.parseOrderFromCitationFragment(args.get("0"))); - } catch (IllegalArgumentException ex) { - log.warn("Could not parse query fragment argument for order", ex); - } - } - - if (args.get("ql") != null) { - try { - query.setQueryLanguage( - QueryLanguage.valueOf(args.get("ql").toUpperCase())); - } catch (IllegalArgumentException ex) { - log.warn( - "Could not parse query fragment argument for the query language", - ex); - } - } - - // full query with given context - ui.getQueryController().setQuery(query); - ui.getQueryController().executeSearch(true, false); - } else if (args.get("q") != null) { - QueryLanguage ql = QueryLanguage.AQL; - if (args.get("ql") != null) { - try { - ql = QueryLanguage.valueOf(args.get("ql").toUpperCase()); - } catch (IllegalArgumentException ex) { - log.warn( - "Could not parse query fragment argument for the query language", - ex); - } - } - // use default context - ui.getQueryController().setQuery(new Query(args.get("q"), ql, corpora)); - ui.getQueryController().executeSearch(true, true); - } - } // end if corpus list from server was non-empty - } // end if there is a corpus definition - } - - public ControlPanel getControlPanel() { - return controlPanel; - } - - public DocBrowserController getDocBrowserController() { - return docBrowserController; - } - - public ResultViewPanel getLastSelectedResultView() { - for (Component c : selectedTabHistory) { - if (c instanceof ResultViewPanel && mainTab.getTab(c) != null) { - return (ResultViewPanel) c; + if (args.get("ql") != null) { + try { + query.setQueryLanguage(QueryLanguage.valueOf(args.get("ql").toUpperCase())); + } catch (IllegalArgumentException ex) { + log.warn("Could not parse query fragment argument for the query language", ex); + } + } + + // full query with given context + ui.getQueryController().setQuery(query); + ui.getQueryController().executeSearch(true, false); + } else if (args.get("q") != null) { + QueryLanguage ql = QueryLanguage.AQL; + if (args.get("ql") != null) { + try { + ql = QueryLanguage.valueOf(args.get("ql").toUpperCase()); + } catch (IllegalArgumentException ex) { + log.warn("Could not parse query fragment argument for the query language", ex); } + } + // use default context + ui.getQueryController().setQuery(new Query(args.get("q"), ql, corpora)); + ui.getQueryController().executeSearch(true, true); } - return null; + } // end if corpus list from server was non-empty + } // end if there is a corpus definition + } + + public ControlPanel getControlPanel() { + return controlPanel; + } + + public DocBrowserController getDocBrowserController() { + return docBrowserController; + } + + public ResultViewPanel getLastSelectedResultView() { + for (Component c : selectedTabHistory) { + if (c instanceof ResultViewPanel && mainTab.getTab(c) != null) { + return (ResultViewPanel) c; + } } + return null; + } - public TabSheet getMainTab() { - return mainTab; - } + public TabSheet getMainTab() { + return mainTab; + } - public MainToolbar getMainToolbar() { - return toolbar; - } + public MainToolbar getMainToolbar() { + return toolbar; + } - public TabSheet getTabSheet() { - return mainTab; - } + public TabSheet getTabSheet() { + return mainTab; + } - public void notifiyQueryStarted() { - if (toolbar != null) { - toolbar.notifiyQueryStarted(); - } + public void notifiyQueryStarted() { + if (toolbar != null) { + toolbar.notifiyQueryStarted(); } + } - @Override - public void notifyCannotPlayMimeType(String mimeType) { - if (mimeType == null) { - return; - } - - if (mimeType.startsWith("audio/ogg") || mimeType.startsWith("video/web")) { - String browserList = ""; - - WebBrowser browser = Page.getCurrent().getWebBrowser(); - - // IE9 users can install a plugin - Set supportedByIE9Plugin = new HashSet<>(); - supportedByIE9Plugin.add("video/webm"); - supportedByIE9Plugin.add("audio/ogg"); - supportedByIE9Plugin.add("video/ogg"); - - if (browser.isIE() && browser.getBrowserMajorVersion() >= 9 - && supportedByIE9Plugin.contains(mimeType)) { - Notification n = new Notification("Media file type unsupported by your browser", - "Please install the WebM plugin for Internet Explorer 9 from " - + "https://tools.google.com/dlpage/webmmf " - + " or use a browser from the following list " - + "(these are known to work with WebM or OGG files)
" - + browserList - + "

Click on this message to hide it", - Notification.Type.WARNING_MESSAGE, true); - n.setDelayMsec(15000); - - n.show(Page.getCurrent()); - } else { - Notification n = new Notification("Media file type unsupported by your browser", - "Please use a browser from the following list " - + "(these are known to work with WebM or OGG files)
" - + browserList - + "

Click on this message to hide it", - Notification.Type.WARNING_MESSAGE, true); - n.setDelayMsec(15000); - n.show(Page.getCurrent()); - } - } else { - Notification.show("Media file type \"" + mimeType + "\" unsupported by your browser!", - "Try to check your browsers documentation how to enable " - + "support for the media type or inform the corpus creator about this problem.", - Notification.Type.WARNING_MESSAGE); - } - + @Override + public void notifyCannotPlayMimeType(String mimeType) { + if (mimeType == null) { + return; } - @Override - public void notifyMightNotPlayMimeType(String mimeType) { - /* - * if(!warnedAboutPossibleMediaFormatProblem) { Notification notify = new - * Notification("Media file type \"" + mimeType + - * "\" might be unsupported by your browser!", - * "This means you might get errors playing this file.

" + - * "If you have problems with this media file:
Try to check your browsers " + - * "documentation how to enable " + - * "support for the media type or inform the corpus creator about this problem." , - * Notification.Type.TRAY_NOTIFICATION, true); notify.setDelayMsec(15000); - * showNotification(notify); warnedAboutPossibleMediaFormatProblem = true; } - */ + if (mimeType.startsWith("audio/ogg") || mimeType.startsWith("video/web")) { + String browserList = ""; + + WebBrowser browser = Page.getCurrent().getWebBrowser(); + + // IE9 users can install a plugin + Set supportedByIE9Plugin = new HashSet<>(); + supportedByIE9Plugin.add("video/webm"); + supportedByIE9Plugin.add("audio/ogg"); + supportedByIE9Plugin.add("video/ogg"); + + if (browser.isIE() && browser.getBrowserMajorVersion() >= 9 + && supportedByIE9Plugin.contains(mimeType)) { + Notification n = new Notification("Media file type unsupported by your browser", + "Please install the WebM plugin for Internet Explorer 9 from " + + "https://tools.google.com/dlpage/webmmf " + + " or use a browser from the following list " + + "(these are known to work with WebM or OGG files)
" + browserList + + "

Click on this message to hide it", + Notification.Type.WARNING_MESSAGE, true); + n.setDelayMsec(15000); + + n.show(Page.getCurrent()); + } else { + Notification n = new Notification("Media file type unsupported by your browser", + "Please use a browser from the following list " + + "(these are known to work with WebM or OGG files)
" + browserList + + "

Click on this message to hide it", + Notification.Type.WARNING_MESSAGE, true); + n.setDelayMsec(15000); + n.show(Page.getCurrent()); + } + } else { + Notification.show("Media file type \"" + mimeType + "\" unsupported by your browser!", + "Try to check your browsers documentation how to enable " + + "support for the media type or inform the corpus creator about this problem.", + Notification.Type.WARNING_MESSAGE); } - @Override - public void onLogin() { - getControlPanel().getCorpusList().updateCorpusSetList(true); - // re-evaluate the fragment in case a corpus is now accessible - evaluateFragment(Page.getCurrent().getUriFragment()); + } + + @Override + public void notifyMightNotPlayMimeType(String mimeType) { + /* + * if(!warnedAboutPossibleMediaFormatProblem) { Notification notify = new + * Notification("Media file type \"" + mimeType + "\" might be unsupported by your browser!", + * "This means you might get errors playing this file.

" + + * "If you have problems with this media file:
Try to check your browsers " + + * "documentation how to enable " + + * "support for the media type or inform the corpus creator about this problem." , + * Notification.Type.TRAY_NOTIFICATION, true); notify.setDelayMsec(15000); + * showNotification(notify); warnedAboutPossibleMediaFormatProblem = true; } + */ + } + + @Override + public void onLogin() { + getControlPanel().getCorpusList().updateCorpusSetList(true); + // re-evaluate the fragment in case a corpus is now accessible + evaluateFragment(Page.getCurrent().getUriFragment()); + } + + @Override + public void onLogout() { + getControlPanel().getCorpusList().updateCorpusSetList(false); + } + + @Override + public void onTabClose(TabSheet tabsheet, Component tabContent) { + // select the tab that was selected before + if (tabsheet == mainTab) { + selectedTabHistory.remove(tabContent); + + if (!selectedTabHistory.isEmpty()) { + // get the last selected tab + Component[] asArray = selectedTabHistory.toArray(new Component[selectedTabHistory.size()]); + mainTab.setSelectedTab(asArray[asArray.length - 1]); + } } - @Override - public void onLogout() { - getControlPanel().getCorpusList().updateCorpusSetList(false); + tabsheet.removeComponent(tabContent); + if (tabContent instanceof FrequencyQueryPanel) { + controlPanel.getQueryPanel().notifyFrequencyTabClose(); } - @Override - public void onTabClose(TabSheet tabsheet, Component tabContent) { - // select the tab that was selected before - if (tabsheet == mainTab) { - selectedTabHistory.remove(tabContent); - - if (!selectedTabHistory.isEmpty()) { - // get the last selected tab - Component[] asArray = - selectedTabHistory.toArray(new Component[selectedTabHistory.size()]); - mainTab.setSelectedTab(asArray[asArray.length - 1]); - } - } - - tabsheet.removeComponent(tabContent); - if (tabContent instanceof FrequencyQueryPanel) { - controlPanel.getQueryPanel().notifyFrequencyTabClose(); - } + } + @Override + public void selectedTabChange(TabSheet.SelectedTabChangeEvent event) { + Component tab = event.getTabSheet().getSelectedTab(); + if (tab != null) { + // first remove the old element to make sure it is added at the end + selectedTabHistory.remove(tab); + selectedTabHistory.add(tab); } + } - @Override - public void selectedTabChange(TabSheet.SelectedTabChangeEvent event) { - Component tab = event.getTabSheet().getSelectedTab(); - if (tab != null) { - // first remove the old element to make sure it is added at the end - selectedTabHistory.remove(tab); - selectedTabHistory.add(tab); - } + public void setToolbar(MainToolbar newToolbar) { + // remove old one if necessary + if (this.toolbar != null) { + removeComponent(this.toolbar); + this.toolbar = null; } - public void setToolbar(MainToolbar newToolbar) { - // remove old one if necessary - if (this.toolbar != null) { - removeComponent(this.toolbar); - this.toolbar = null; - } - - // add new toolbar - if (newToolbar != null) { - this.toolbar = newToolbar; - addComponent(this.toolbar, 0, 0, 1, 0); - } + // add new toolbar + if (newToolbar != null) { + this.toolbar = newToolbar; + addComponent(this.toolbar, 0, 0, 1, 0); } + } - /** - * Updates the browser address bar with the current query parameters and the query itself. - * - * This is for convenient reloading the vaadin app and easy copying citation links. - * - * @param q The query where the parameters are extracted from. - */ - public void updateFragment(DisplayedResultQuery q) { - // set our fragment - lastEvaluatedFragment = q.toCitationFragment(); - UI.getCurrent().getPage().setUriFragment(lastEvaluatedFragment, false); - - // reset title - Page.getCurrent().setTitle( - ui.getInstanceConfig().getInstanceDisplayName() + " (ANNIS Corpus Search)"); - } + /** + * Updates the browser address bar with the current query parameters and the query itself. + * + * This is for convenient reloading the vaadin app and easy copying citation links. + * + * @param q The query where the parameters are extracted from. + */ + public void updateFragment(DisplayedResultQuery q) { + // set our fragment + lastEvaluatedFragment = q.toCitationFragment(); + UI.getCurrent().getPage().setUriFragment(lastEvaluatedFragment, false); - @Override - public void updateSidebarState(SidebarState state) { - if (controlPanel != null && state != null) { - controlPanel.setVisible(state.isSidebarVisible()); + // reset title + Page.getCurrent() + .setTitle(ui.getInstanceConfig().getInstanceDisplayName() + " (ANNIS Corpus Search)"); + } - // set cookie - ui.getSettings().set("annis-sidebar-state", state.name(), 30); - } - } + public void updateSidebarState(SidebarState state) { + if (controlPanel != null && state != null) { + controlPanel.setVisible(state.isSidebarVisible()); - @Override - public void uriFragmentChanged(Page.UriFragmentChangedEvent event) { - evaluateFragment(event.getUriFragment()); + // set cookie + ui.getSettings().set("annis-sidebar-state", state.name(), 30); } + } - public HelpPanel getHelpPanel() { - return helpPanel; - } + @Override + public void uriFragmentChanged(Page.UriFragmentChangedEvent event) { + evaluateFragment(event.getUriFragment()); + } + + public HelpPanel getHelpPanel() { + return helpPanel; + } } diff --git a/src/main/java/org/corpus_tools/annis/gui/ServiceStarter.java b/src/main/java/org/corpus_tools/annis/gui/ServiceStarter.java index 4a233926f2..beeb06936e 100644 --- a/src/main/java/org/corpus_tools/annis/gui/ServiceStarter.java +++ b/src/main/java/org/corpus_tools/annis/gui/ServiceStarter.java @@ -120,7 +120,7 @@ private void startService() { ProcessBuilder backgroundProcessBuilder; if (SystemUtils.IS_OS_MAC_OSX) { - // On MacOS has several processor architectors, but allows to emulate x86_64 processors + // On MacOS has several processor architectures, but allows to emulate x86_64 processors // with the Rosetta tool. We use the `arch` helper program to trigger emulation if // necessary. This is necessary when Java support the native processor architecture and // thus is not emulated yet. @@ -137,11 +137,6 @@ private void startService() { // our log this.tReaderOut = createOutputWatcherThread(backgroundProcess.getInputStream(), false); this.tReaderErr = createOutputWatcherThread(backgroundProcess.getErrorStream(), true); - - // Use the provided service configuration to get the correct port - TomlMapper mapper = new TomlMapper(); - Map parsedServiceConfig = mapper.readValue(serviceConfigFile, Map.class); - config.setWebserviceUrl(getServiceURL(parsedServiceConfig)); } } } catch (final IOException ex) { @@ -208,21 +203,8 @@ private Thread createOutputWatcherThread(InputStream stream, boolean isError) { } - protected String getServiceURL(Map serviceConfig) { - long port = 5711l; - Object bindSection = serviceConfig.get("bind"); - if (bindSection instanceof Map) { - @SuppressWarnings("rawtypes") - Object portRaw = ((Map) bindSection).get("port"); - if (portRaw instanceof Long) { - port = (Long) portRaw; - } - } - return "http://localhost:" + port + "/v1"; - } - @SuppressWarnings("unchecked") - protected File getServiceConfig() throws IOException { + public File getServiceConfig() throws IOException { File existingConfigFile = new File(config.getWebserviceConfig()); final TomlMapper mapper = new TomlMapper(); diff --git a/src/main/java/org/corpus_tools/annis/gui/ServiceStarterDesktop.java b/src/main/java/org/corpus_tools/annis/gui/ServiceStarterDesktop.java index ec6cf68711..ced8ae6055 100644 --- a/src/main/java/org/corpus_tools/annis/gui/ServiceStarterDesktop.java +++ b/src/main/java/org/corpus_tools/annis/gui/ServiceStarterDesktop.java @@ -51,170 +51,169 @@ @Profile("desktop") public class ServiceStarterDesktop extends ServiceStarter { // NO_UCD (unused code) - private static final String USER_NAME = "desktop"; - private static final Logger log = LoggerFactory.getLogger(ServiceStarterDesktop.class); - private final String secret = RandomStringUtils.randomAlphanumeric(50); - private Optional desktopUserCredentials = Optional.empty(); - - @Value("${server.port}") - private String serverPort; - - @Autowired - private Environment env; - - - /** - * Overwrite the existing configuration file and add a temporary user for the desktop. - */ - @Override - protected File getServiceConfig() throws IOException { - TomlMapper mapper = new TomlMapper(); - @SuppressWarnings("unchecked") - Map config = mapper.readValue(super.getServiceConfig(), Map.class); - - // Add it to the configuration - Map tokenVerification = new LinkedHashMap<>(); - tokenVerification.put("type", "HS256"); - tokenVerification.put("secret", this.secret); - - // Add the new auth configuration (removing all existing settings in [auth]) - Map auth = new HashMap<>(); - auth.put("token_verification", tokenVerification); - config.put("auth", auth); - - File temporaryFile = File.createTempFile("annis-service-config-desktop-", ".toml"); - temporaryFile.deleteOnExit(); - mapper.writeValue(temporaryFile, config); - return temporaryFile; - } - - private void showApplicationWindow() { - try { - UIManager.setLookAndFeel(new NimbusLookAndFeel()); - } catch (UnsupportedLookAndFeelException ex) { - log.warn("Look and feel not supported", ex); - } - - // Create a window where log messages and a link to the UI can be shown - // This also allows to exit the application when no terminal is shown. - JFrame mainFrame = new JFrame("ANNIS Desktop"); - BorderLayout mainLayout = new BorderLayout(); - mainFrame.getContentPane().setLayout(mainLayout); - mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - mainFrame.setLocationByPlatform(true); - - final String webURL = "http://localhost:" + serverPort; - JButton btLaunch = new JButton(); - - btLaunch.setMnemonic('u'); - btLaunch.setForeground(Color.blue); - btLaunch.setText("Open " + webURL + " in browser"); - btLaunch.setEnabled(true); - btLaunch.setName("btLaunch"); - btLaunch.addActionListener(evt -> openBrowser(webURL)); - btLaunch.setPreferredSize(new Dimension(300, 60)); - mainFrame.getContentPane().add(btLaunch, BorderLayout.CENTER); - - JButton btExit = new JButton("Exit"); - btExit.addActionListener((evt) -> { - System.exit(0); - }); - mainFrame.getContentPane().add(btExit, BorderLayout.PAGE_END); - - mainFrame.pack(); - - // Set icon for window - Integer[] sizes = new Integer[] {192, 128, 64, 48, 32, 16, 14}; - List allImages = new LinkedList(); - - for (int s : sizes) { - try { - BufferedImage imgIcon = - ImageIO.read(ServiceStarterDesktop.class.getResource("logo/annis_" + s + ".png")); - allImages.add(imgIcon); - } catch (IOException ex) { - log.error(null, ex); - } - } - mainFrame.setIconImages(allImages); - - mainFrame.setVisible(true); - } - - @Override - public void onApplicationEvent(ApplicationReadyEvent event) { - super.onApplicationEvent(event); - - List roles = Arrays.asList("admin"); - Instant issuedAt = Instant.now(); - Instant expiresAt = Instant.now().plus(7l, ChronoUnit.DAYS); - - // Use the secret to sign a new JWT token with admin rights - String signedToken = JWT.create().withSubject(USER_NAME) - .withClaim(SecurityConfiguration.ROLES_CLAIM, roles).withExpiresAt(Date.from(expiresAt)) - .withIssuedAt(Date.from(issuedAt)).sign(Algorithm.HMAC256(this.secret)); - - // Create the needed information for to represent this token as OIDC token in - // Spring - // security - List grantedAuthorities = - Arrays.asList(new SimpleGrantedAuthority("admin")); - LinkedHashMap claims = new LinkedHashMap<>(); - claims.put(SecurityConfiguration.ROLES_CLAIM, roles); - claims.put("sub", USER_NAME); - DefaultOAuth2User user = new DefaultOAuth2User(grantedAuthorities, claims, "sub"); - - this.desktopUserCredentials = Optional.of(new DesktopAuthentication(user, signedToken)); - - // Open the application in the browser - String webURL = "http://localhost:" + serverPort; - boolean isRunningHeadless = Arrays.stream(env.getActiveProfiles()).anyMatch("headless"::equals); - Desktop desktop = - !isRunningHeadless && Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; - if (desktop == null) { - log.warn( - "ANNIS is running in desktop mode, but no desktop has been detected. You can open {} manually.", - webURL); - } else if ("amd64".equals(SystemUtils.OS_ARCH) || "x86_64".equals(SystemUtils.OS_ARCH) - || SystemUtils.IS_OS_MAC_OSX) { - showApplicationWindow(); - openBrowser(webURL); - } else { - // No supported system detected, show error and exit - JOptionPane.showMessageDialog(null, - "ANNIS can only be run on 64 bit operating systems (\"amd64\" or \"x86_64\")\n" - + "and with a 64 bit version of Java,\n" - + "but this computer is reported as architecture " + SystemUtils.OS_ARCH + "!\n\n" - + "A common cause is that a 32 bit version of Java is installed.\n" - + "Make sure to install a 64 bit Java 11\n" + "e.g. from https://adoptium.net", - "Cannot run ANNIS on incompatible operating system", JOptionPane.ERROR_MESSAGE); - System.exit(64); - } - } - - private void openBrowser(String webURL) { - log.info("Opening {} in browser", webURL); - boolean supported = true; - try { - supported = com.github.jjYBdx4IL.utils.awt.Desktop.browse(new URI(webURL)); - } catch (URISyntaxException ex) { - log.error("Could not open " + webURL + " in browser.", ex); - supported = false; - } catch (UnsupportedOperationException ex) { - supported = false; - } - if (!supported) { - log.warn("Opening the browser is unsupported on this platform. " - + "Please open {} in your browser manually.", webURL); - JOptionPane.showMessageDialog(null, - "Cannot open the browser automatically. You can manually browse to " + webURL - + " top open the ANNIS interface."); - } - } - - @Override - public Optional getDesktopUserToken() { - return desktopUserCredentials; - } + private static final String USER_NAME = "desktop"; + private static final Logger log = LoggerFactory.getLogger(ServiceStarterDesktop.class); + private final String secret = RandomStringUtils.randomAlphanumeric(50); + private Optional desktopUserCredentials = Optional.empty(); + + @Value("${server.port}") + private String serverPort; + + @Autowired + private Environment env; + + /** + * Overwrite the existing configuration file and add a temporary user for the + * desktop. + */ + @Override + public File getServiceConfig() throws IOException { + TomlMapper mapper = new TomlMapper(); + @SuppressWarnings("unchecked") + Map config = mapper.readValue(super.getServiceConfig(), Map.class); + + // Add it to the configuration + Map tokenVerification = new LinkedHashMap<>(); + tokenVerification.put("type", "HS256"); + tokenVerification.put("secret", this.secret); + + // Add the new auth configuration (removing all existing settings in [auth]) + Map auth = new HashMap<>(); + auth.put("token_verification", tokenVerification); + config.put("auth", auth); + + File temporaryFile = File.createTempFile("annis-service-config-desktop-", ".toml"); + temporaryFile.deleteOnExit(); + mapper.writeValue(temporaryFile, config); + return temporaryFile; + } + + private void showApplicationWindow() { + try { + UIManager.setLookAndFeel(new NimbusLookAndFeel()); + } catch (UnsupportedLookAndFeelException ex) { + log.warn("Look and feel not supported", ex); + } + + // Create a window where log messages and a link to the UI can be shown + // This also allows to exit the application when no terminal is shown. + JFrame mainFrame = new JFrame("ANNIS Desktop"); + BorderLayout mainLayout = new BorderLayout(); + mainFrame.getContentPane().setLayout(mainLayout); + mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + mainFrame.setLocationByPlatform(true); + + final String webURL = "http://localhost:" + serverPort; + JButton btLaunch = new JButton(); + + btLaunch.setMnemonic('u'); + btLaunch.setForeground(Color.blue); + btLaunch.setText("Open " + webURL + " in browser"); + btLaunch.setEnabled(true); + btLaunch.setName("btLaunch"); + btLaunch.addActionListener(evt -> openBrowser(webURL)); + btLaunch.setPreferredSize(new Dimension(300, 60)); + mainFrame.getContentPane().add(btLaunch, BorderLayout.CENTER); + + JButton btExit = new JButton("Exit"); + btExit.addActionListener((evt) -> { + System.exit(0); + }); + mainFrame.getContentPane().add(btExit, BorderLayout.PAGE_END); + + mainFrame.pack(); + + // Set icon for window + Integer[] sizes = new Integer[] { 192, 128, 64, 48, 32, 16, 14 }; + List allImages = new LinkedList(); + + for (int s : sizes) { + try { + BufferedImage imgIcon = ImageIO + .read(ServiceStarterDesktop.class.getResource("logo/annis_" + s + ".png")); + allImages.add(imgIcon); + } catch (IOException ex) { + log.error(null, ex); + } + } + mainFrame.setIconImages(allImages); + + mainFrame.setVisible(true); + } + + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + super.onApplicationEvent(event); + + // Open the application in the browser + String webURL = "http://localhost:" + serverPort; + boolean isRunningHeadless = Arrays.stream(env.getActiveProfiles()).anyMatch("headless"::equals); + Desktop desktop = !isRunningHeadless && Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; + if (desktop == null) { + log.warn("ANNIS is running in desktop mode, but no desktop has been detected. You can open {} manually.", + webURL); + } else if ("amd64".equals(SystemUtils.OS_ARCH) || "x86_64".equals(SystemUtils.OS_ARCH) + || SystemUtils.IS_OS_MAC_OSX) { + showApplicationWindow(); + openBrowser(webURL); + } else { + // No supported system detected, show error and exit + JOptionPane.showMessageDialog(null, + "ANNIS can only be run on 64 bit operating systems (\"amd64\" or \"x86_64\")\n" + + "and with a 64 bit version of Java,\n" + "but this computer is reported as architecture " + + SystemUtils.OS_ARCH + "!\n\n" + + "A common cause is that a 32 bit version of Java is installed.\n" + + "Make sure to install a 64 bit Java 11\n" + "e.g. from https://adoptium.net", + "Cannot run ANNIS on incompatible operating system", JOptionPane.ERROR_MESSAGE); + System.exit(64); + } + } + + private void openBrowser(String webURL) { + log.info("Opening {} in browser", webURL); + boolean supported = true; + try { + supported = com.github.jjYBdx4IL.utils.awt.Desktop.browse(new URI(webURL)); + } catch (URISyntaxException ex) { + log.error("Could not open " + webURL + " in browser.", ex); + supported = false; + } catch (UnsupportedOperationException ex) { + supported = false; + } + if (!supported) { + log.warn("Opening the browser is unsupported on this platform. " + + "Please open {} in your browser manually.", webURL); + JOptionPane.showMessageDialog(null, "Cannot open the browser automatically. You can manually browse to " + + webURL + " top open the ANNIS interface."); + } + } + + @Override + public Optional getDesktopUserToken() { + + if (desktopUserCredentials.isEmpty()) { + List roles = Arrays.asList("admin"); + Instant issuedAt = Instant.now(); + Instant expiresAt = Instant.now().plus(7l, ChronoUnit.DAYS); + + // Use the secret to sign a new JWT token with admin rights + String signedToken = JWT.create().withSubject(USER_NAME).withClaim(SecurityConfiguration.ROLES_CLAIM, roles) + .withExpiresAt(Date.from(expiresAt)).withIssuedAt(Date.from(issuedAt)) + .sign(Algorithm.HMAC256(this.secret)); + + // Create the needed information for to represent this token as OIDC token in + // Spring security + List grantedAuthorities = Arrays.asList(new SimpleGrantedAuthority("admin")); + LinkedHashMap claims = new LinkedHashMap<>(); + claims.put(SecurityConfiguration.ROLES_CLAIM, roles); + claims.put("sub", USER_NAME); + DefaultOAuth2User user = new DefaultOAuth2User(grantedAuthorities, claims, "sub"); + + DesktopAuthentication authentication = new DesktopAuthentication(user, signedToken); + this.desktopUserCredentials = Optional.of(authentication); + } + + return desktopUserCredentials; + } } diff --git a/src/main/java/org/corpus_tools/annis/gui/Sidebar.java b/src/main/java/org/corpus_tools/annis/gui/Sidebar.java deleted file mode 100644 index 30719da640..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/Sidebar.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2014 Corpuslinguistic working group Humboldt University Berlin. - * - * 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.corpus_tools.annis.gui; - -/** - * A general interface for a sidebar which can be hidden. - * - * @author Thomas Krause {@literal } - */ -public interface Sidebar { - /** - * update controls according to new state - * - * @param state - */ - public void updateSidebarState(SidebarState state); -} diff --git a/src/main/java/org/corpus_tools/annis/gui/UnsupportedQueryUI.java b/src/main/java/org/corpus_tools/annis/gui/UnsupportedQueryUI.java index d0b0e2a693..4eca1aa87f 100644 --- a/src/main/java/org/corpus_tools/annis/gui/UnsupportedQueryUI.java +++ b/src/main/java/org/corpus_tools/annis/gui/UnsupportedQueryUI.java @@ -21,11 +21,9 @@ import com.vaadin.ui.Panel; import com.vaadin.ui.declarative.Design; import javax.servlet.ServletContext; -import org.corpus_tools.annis.ApiClient; -import org.corpus_tools.annis.gui.security.AuthenticationSuccessListener; -import org.corpus_tools.annis.gui.security.AutoTokenRefreshClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; +import org.springframework.web.reactive.function.client.WebClient; @SpringUI(path = "/unsupported-query") @Widgetset("org.corpus_tools.annis.gui.widgets.gwt.AnnisWidgetSet") @@ -76,19 +74,17 @@ public void setUrl(String url) { @Autowired private UIConfig config; - - private final AuthenticationSuccessListener authListener; + + @Autowired + private WebClient webClient; private UnsupportedQueryPanel panel; protected Page overwrittenPage; - @Autowired - public UnsupportedQueryUI(ServiceStarter serviceStarter, - AuthenticationSuccessListener authListener) { - super(URL_PREFIX, serviceStarter, authListener); - this.authListener = authListener; + public UnsupportedQueryUI(ServiceStarter serviceStarter) { + super(URL_PREFIX, serviceStarter); } @Override @@ -127,9 +123,9 @@ public Page getPage() { return overwrittenPage; } } - + @Override - public ApiClient getClient() { - return new AutoTokenRefreshClient(this, this.authListener); + public WebClient getWebClient() { + return webClient; } } diff --git a/src/main/java/org/corpus_tools/annis/gui/VisualizationToggle.java b/src/main/java/org/corpus_tools/annis/gui/VisualizationToggle.java deleted file mode 100644 index 117f9402d5..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/VisualizationToggle.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012 SFB 632. - * - * 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.corpus_tools.annis.gui; - -import java.io.Serializable; -import org.corpus_tools.annis.gui.visualizers.LoadableVisualizer; - -/** - * - * @author thomas - */ -public interface VisualizationToggle extends Serializable { - /** - * Shows and hides the visualizer. - * - * @param visible - */ - public void toggleVisualizer(boolean visible, LoadableVisualizer.Callback callback); - - /** Returns wether the visualization is visible. */ - public boolean visualizerIsVisible(); -} diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/AdminView.java b/src/main/java/org/corpus_tools/annis/gui/admin/AdminView.java index 263a09d212..9f0a83dfc5 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/AdminView.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/AdminView.java @@ -18,6 +18,7 @@ import com.vaadin.navigator.View; import com.vaadin.navigator.ViewChangeListener; import com.vaadin.server.Page; +import com.vaadin.spring.annotation.SpringView; import com.vaadin.ui.Component; import com.vaadin.ui.Notification; import com.vaadin.ui.TabSheet; @@ -26,12 +27,8 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; -import org.corpus_tools.annis.ApiClient; import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.Background; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.LoginListener; -import org.corpus_tools.annis.gui.MainToolbar; import org.corpus_tools.annis.gui.admin.controller.CorpusController; import org.corpus_tools.annis.gui.admin.controller.GroupController; import org.corpus_tools.annis.gui.admin.model.ApiClientProvider; @@ -40,13 +37,18 @@ import org.corpus_tools.annis.gui.admin.reflinks.MigrationPanel; import org.corpus_tools.annis.gui.admin.reflinks.ReferenceLinkEditor; import org.corpus_tools.annis.gui.admin.view.UIView; +import org.corpus_tools.annis.gui.components.MainToolbar; +import org.corpus_tools.annis.gui.security.LoginListener; +import org.corpus_tools.annis.gui.util.Helper; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.reactive.function.client.WebClient; /** * * @author Thomas Krause {@literal } */ +@SpringView public class AdminView extends VerticalLayout implements View, UIView, LoginListener, TabSheet.SelectedTabChangeListener, ApiClientProvider { @@ -198,12 +200,6 @@ private String getFragmentForComponent(Component c) { return ""; } - - @Override - public void invalidateClient() { - // The current client implementation the client is always fresh - } - @Override public void onLogin() { for (UIView.Listener l : listeners) { @@ -280,10 +276,9 @@ public void showInfo(String info, String description) { public void showWarning(String error, String description) { Notification.show(error, description, Notification.Type.WARNING_MESSAGE); } - @Override - public ApiClient getClient() { - return ui.getClient(); + public WebClient getWebClient() { + return ui.getWebClient(); } } diff --git a/src/main/java/org/corpus_tools/annis/gui/CriticalServiceQueryException.java b/src/main/java/org/corpus_tools/annis/gui/admin/CriticalServiceQueryException.java similarity index 90% rename from src/main/java/org/corpus_tools/annis/gui/CriticalServiceQueryException.java rename to src/main/java/org/corpus_tools/annis/gui/admin/CriticalServiceQueryException.java index 48753b88a0..736dd9fab5 100644 --- a/src/main/java/org/corpus_tools/annis/gui/CriticalServiceQueryException.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/CriticalServiceQueryException.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.admin; + +import org.corpus_tools.annis.gui.ServiceQueryException; /** * diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/ImportPanel.java b/src/main/java/org/corpus_tools/annis/gui/admin/ImportPanel.java index b1e121764f..0cb551c870 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/ImportPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/ImportPanel.java @@ -13,6 +13,7 @@ */ package org.corpus_tools.annis.gui.admin; +import com.google.gson.Gson; import com.vaadin.icons.VaadinIcons; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; @@ -33,19 +34,20 @@ import java.io.OutputStream; import java.text.DecimalFormat; import java.util.List; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.ApiResponse; -import org.corpus_tools.annis.JSON; -import org.corpus_tools.annis.api.AdministrationApi; import org.corpus_tools.annis.api.model.ImportResult; import org.corpus_tools.annis.api.model.Job; import org.corpus_tools.annis.api.model.Job.StatusEnum; import org.corpus_tools.annis.gui.Background; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.LoginListener; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.security.LoginListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * @@ -55,16 +57,16 @@ public class ImportPanel extends Panel implements Upload.ProgressListener, Uploa Upload.StartedListener, Upload.Receiver, LoginListener { private class WaitForFinishRunner implements Runnable { - private final UI ui; + private final CommonUI ui; private final String uuid; private int currentMessageIndex = 0; - private JSON json; + private Gson json; - public WaitForFinishRunner(String uuid, UI ui) { + public WaitForFinishRunner(String uuid, CommonUI ui) { this.ui = ui; this.uuid = uuid; - this.json = new JSON(); + this.json = new Gson(); } private void appendFromBackground(List message) { @@ -90,24 +92,24 @@ private void outputNewMessages(List allMessages) { @Override public void run() { - AdministrationApi api = new AdministrationApi(Helper.getClient(ui)); + WebClient client = ui.getWebClient(); // check the overall status Job job = null; try { do { job = null; - ApiResponse response = - api.getApiClient().execute(api.getJobCall(uuid, null), String.class); + ResponseEntity response = + client.get().uri("/jobs/{uuid}", uuid).retrieve().toEntity(String.class).block(); // If the response type returns an object of type Job, get it here - int statusCode = response.getStatusCode(); - if (statusCode == HttpStatus.ACCEPTED.value()) { - job = json.deserialize(response.getData(), Job.class); + HttpStatus statusCode = response.getStatusCode(); + if (statusCode == HttpStatus.ACCEPTED) { + job = json.fromJson(response.getBody(), Job.class); outputNewMessages(job.getMessages()); - } else if (statusCode == HttpStatus.OK.value()) { + } else if (statusCode == HttpStatus.OK) { // The last messages are given as array of strings - String[] finishMessages = json.deserialize(response.getData(), String[].class); + String[] finishMessages = json.fromJson(response.getBody(), String[].class); for (int i = 0; i < finishMessages.length; i++) { appendFromBackground(finishMessages[i]); } @@ -137,15 +139,15 @@ public void run() { } catch (InterruptedException ex) { log.error(null, ex); Thread.currentThread().interrupt(); - } catch (ApiException ex) { - if (ex.getCode() == HttpStatus.GONE.value()) { + } catch (WebClientResponseException ex) { + if (ex.getStatusCode() == HttpStatus.GONE) { // Decode the Job object with its included error messages - job = json.deserialize(ex.getResponseBody(), Job.class); + job = json.fromJson(ex.getResponseBodyAsString(), Job.class); outputNewMessages(job.getMessages()); } else { appendFromBackground( "Exception while polling for import status: " + ex.getMessage() + "\n" - + ex.getResponseBody()); + + ex.getResponseBodyAsString()); } ui.access(() -> { progress.setVisible(false); @@ -289,19 +291,23 @@ private void setLogVisible(boolean visible) { } } - private void startImport() { - - AdministrationApi api = new AdministrationApi(Helper.getClient(UI.getCurrent())); + private void startImport(CommonUI ui) { try { - ApiResponse response = - api.importPostWithHttpInfo(temporaryCorpusFile, cbOverwrite.getValue()); - - - if (response.getStatusCode() == HttpStatus.ACCEPTED.value()) { - String uuid = response.getData().getUuid(); + FileSystemResource fileResource = new FileSystemResource(temporaryCorpusFile); + ResponseEntity response = ui.getWebClient().post() + .uri(ub -> ub.path("/import").queryParam("override_existing", cbOverwrite.getValue()) + .build()) + .body(BodyInserters.fromResource(fileResource)).retrieve() + .toEntity(ImportResult.class).block(); + + if (response == null) { + upload.setEnabled(true); + progress.setVisible(false); + appendMessage("Could not start import."); + } else if (response.getStatusCode() == HttpStatus.ACCEPTED) { + String uuid = response.getBody().getUuid(); appendMessage("Import requested, update UUID is " + uuid); - UI ui = UI.getCurrent(); Background.run(new WaitForFinishRunner(uuid, ui)); } else { @@ -309,10 +315,11 @@ private void startImport() { progress.setVisible(false); appendMessage("Error (response code " + response.getStatusCode() + ")"); } - } catch (ApiException ex) { + } catch (WebClientResponseException ex) { upload.setEnabled(true); progress.setVisible(false); - appendMessage("Error (response code " + ex.getCode() + "): " + ex.getResponseBody()); + appendMessage( + "Error (response code " + ex.getRawStatusCode() + "): " + ex.getResponseBodyAsString()); } @@ -333,9 +340,11 @@ public void updateProgress(long readBytes, long contentLength) { @Override public void uploadFinished(Upload.FinishedEvent event) { - - appendMessage("Finished upload, starting import"); - startImport(); + UI ui = UI.getCurrent(); + if (ui instanceof CommonUI) { + appendMessage("Finished upload, starting import"); + startImport((CommonUI) ui); + } } @Override diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/PopupTwinColumnSelect.java b/src/main/java/org/corpus_tools/annis/gui/admin/PopupTwinColumnSelect.java index 1c36d6165e..fd86da4f54 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/PopupTwinColumnSelect.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/PopupTwinColumnSelect.java @@ -29,7 +29,7 @@ import java.util.Collection; import java.util.Set; import java.util.TreeSet; -import org.corpus_tools.annis.gui.CaseSensitiveOrder; +import org.corpus_tools.annis.gui.converter.CaseSensitiveOrder; import org.corpus_tools.annis.gui.converter.CommaSeperatedStringConverterSet; import org.corpus_tools.annis.gui.converter.TreeSetConverter; diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/controller/CorpusController.java b/src/main/java/org/corpus_tools/annis/gui/admin/controller/CorpusController.java index c379412d8c..562a8f14dd 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/controller/CorpusController.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/controller/CorpusController.java @@ -20,8 +20,8 @@ import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Set; -import org.corpus_tools.annis.gui.CriticalServiceQueryException; import org.corpus_tools.annis.gui.ServiceQueryException; +import org.corpus_tools.annis.gui.admin.CriticalServiceQueryException; import org.corpus_tools.annis.gui.admin.model.CorpusManagement; import org.corpus_tools.annis.gui.admin.view.CorpusListView; import org.corpus_tools.annis.gui.admin.view.UIView; @@ -96,9 +96,6 @@ public void loadedTab(Object selectedTab) { @Override public void loginChanged(boolean isLoggedIn) { this.isLoggedIn = isLoggedIn; - if (model.getClientProvider() != null) { - model.getClientProvider().invalidateClient(); - } if (isLoggedIn && viewIsActive) { fetchFromService(); } else { diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/controller/GroupController.java b/src/main/java/org/corpus_tools/annis/gui/admin/controller/GroupController.java index 6070dba92d..7d594efed8 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/controller/GroupController.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/controller/GroupController.java @@ -19,8 +19,8 @@ import java.util.Set; import java.util.TreeSet; import org.corpus_tools.annis.api.model.Group; -import org.corpus_tools.annis.gui.CriticalServiceQueryException; import org.corpus_tools.annis.gui.ServiceQueryException; +import org.corpus_tools.annis.gui.admin.CriticalServiceQueryException; import org.corpus_tools.annis.gui.admin.model.CorpusManagement; import org.corpus_tools.annis.gui.admin.model.GroupManagement; import org.corpus_tools.annis.gui.admin.view.GroupListView; @@ -154,12 +154,6 @@ public void loadedTab(Object selectedTab) { @Override public void loginChanged(boolean isLoggedIn) { this.isLoggedIn = isLoggedIn; - if (model.getWebResourceProvider() != null) { - model.getWebResourceProvider().invalidateClient(); - } - if (corpusModel.getClientProvider() != null) { - corpusModel.getClientProvider().invalidateClient(); - } if (isLoggedIn && viewIsActive) { fetchDataFromService(); } else { diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/model/ApiClientProvider.java b/src/main/java/org/corpus_tools/annis/gui/admin/model/ApiClientProvider.java index 63d67810d9..d0b0a5ff92 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/model/ApiClientProvider.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/model/ApiClientProvider.java @@ -1,46 +1,40 @@ /* * Copyright 2015 Corpuslinguistic working group Humboldt University Berlin. * - * 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 + * 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 + * 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. + * 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.corpus_tools.annis.gui.admin.model; import java.io.Serializable; -import org.corpus_tools.annis.ApiClient; +import org.apache.catalina.WebResource; +import org.springframework.web.reactive.function.client.WebClient; /** * Defines a way to get a {@link WebResource} needed to make REST calls. * - * This interface extends {@link Serializable} so it can be included in other - * serializable classes (the {@link WebResource} itself is not serializable). + * This interface extends {@link Serializable} so it can be included in other serializable classes + * (the {@link WebResource} itself is not serializable). * * @author Thomas Krause {@literal } */ public interface ApiClientProvider extends Serializable { - /** - * Returns a (possible cached) {@link ApiClient}. - * - * If the the user is authenticated this includes the authentication information. - * - * @return The {@link ApiClient} but never {@code null}. - */ - public ApiClient getClient(); + /** + * Returns a {@link WebClient}. + * + * If the the user is authenticated this includes the authentication information. + * + * @return The {@link WebClient} but never {@code null}. + */ + public WebClient getWebClient(); - /** - * Called when the client got invalid. E.g. the login might have changed and the cached resource - * can't be used any longer. Will force the provider to update the client. - */ - public void invalidateClient(); } diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/model/CorpusManagement.java b/src/main/java/org/corpus_tools/annis/gui/admin/model/CorpusManagement.java index 6c1ca8dbd1..67956e7824 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/model/CorpusManagement.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/model/CorpusManagement.java @@ -15,14 +15,16 @@ import com.google.common.collect.ImmutableList; import java.io.Serializable; +import java.util.List; import java.util.TreeSet; -import javax.ws.rs.core.Response; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.gui.CriticalServiceQueryException; import org.corpus_tools.annis.gui.ServiceQueryException; +import org.corpus_tools.annis.gui.admin.CriticalServiceQueryException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * A model that handles the corpus list @@ -46,13 +48,14 @@ public void clear() { public void delete(String corpusName) throws CriticalServiceQueryException, ServiceQueryException { if (clientProvider != null) { - CorporaApi api = new CorporaApi(clientProvider.getClient()); + WebClient client = clientProvider.getWebClient(); try { - api.deleteCorpus(corpusName); - } catch (ApiException ex) { - if (ex.getCode() == Response.Status.UNAUTHORIZED.getStatusCode()) { + client.delete().uri("/corpora/{corpus}", corpusName).retrieve().bodyToMono(Void.class) + .block(); + } catch (WebClientResponseException ex) { + if (ex.getStatusCode() == HttpStatus.UNAUTHORIZED) { throw new CriticalServiceQueryException("You are not authorized to delete a corpus"); - } else if (ex.getCode() == Response.Status.NOT_FOUND.getStatusCode()) { + } else if (ex.getStatusCode() == HttpStatus.NOT_FOUND) { throw new ServiceQueryException("Corpus with name " + corpusName + " not found"); } else { log.error(null, ex); @@ -67,11 +70,12 @@ public void fetchFromService() throws CriticalServiceQueryException, ServiceQuer if (clientProvider != null) { corpora.clear(); - CorporaApi api = new CorporaApi(clientProvider.getClient()); try { - corpora.addAll(api.listCorpora()); - } catch (ApiException ex) { - if (ex.getCode() == Response.Status.UNAUTHORIZED.getStatusCode()) { + List queriedCorpora = clientProvider.getWebClient().get().uri("/corpora").retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}).block(); + corpora.addAll(queriedCorpora); + } catch (WebClientResponseException ex) { + if (ex.getStatusCode() == HttpStatus.UNAUTHORIZED) { throw new CriticalServiceQueryException("You are not authorized to get the corpus list."); } else { log.error(null, ex); diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/model/GroupManagement.java b/src/main/java/org/corpus_tools/annis/gui/admin/model/GroupManagement.java index bda08db6ce..95514c7674 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/model/GroupManagement.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/model/GroupManagement.java @@ -17,15 +17,14 @@ import com.google.common.collect.ImmutableSet; import java.io.Serializable; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.TreeMap; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.AdministrationApi; import org.corpus_tools.annis.api.model.Group; -import org.corpus_tools.annis.gui.CaseSensitiveOrder; +import org.corpus_tools.annis.gui.converter.CaseSensitiveOrder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * A model for groups. @@ -48,11 +47,12 @@ public void clear() { public void createOrUpdateGroup(Group newGroup) { if (apiClientProvider != null) { - AdministrationApi api = new AdministrationApi(apiClientProvider.getClient()); + WebClient client = apiClientProvider.getWebClient(); try { - api.putGroup(newGroup.getName(), newGroup); + client.put().uri("/groups/{name}", newGroup.getName()).bodyValue(newGroup).retrieve() + .bodyToMono(Void.class).block(); groups.put(newGroup.getName(), newGroup); - } catch (ApiException ex) { + } catch (WebClientResponseException ex) { log.warn("Could not update group", ex); } @@ -61,11 +61,11 @@ public void createOrUpdateGroup(Group newGroup) { public void deleteGroup(String groupName) { if (apiClientProvider != null) { - AdministrationApi api = new AdministrationApi(apiClientProvider.getClient()); + WebClient client = apiClientProvider.getWebClient(); try { - api.deleteGroup(groupName); + client.delete().uri("/groups/{name}", groupName).retrieve().bodyToMono(Void.class).block(); groups.remove(groupName); - } catch (ApiException ex) { + } catch (WebClientResponseException ex) { log.warn("Could not update group", ex); } @@ -74,16 +74,16 @@ public void deleteGroup(String groupName) { public boolean fetchFromService() { if (apiClientProvider != null) { - AdministrationApi api = new AdministrationApi(apiClientProvider.getClient()); + WebClient client = apiClientProvider.getWebClient(); groups.clear(); try { - - List list = api.listGroups(); - for (Group g : list) { + Iterable remoteGroups = + client.get().uri("/groups").retrieve().bodyToFlux(Group.class).toIterable(); + for (Group g : remoteGroups) { groups.put(g.getName(), g); } return true; - } catch (ApiException ex) { + } catch (WebClientResponseException ex) { log.error("Could not get the list of groups", ex); } } diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationCallback.java b/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationCallback.java index 712a9e2020..9287ed937d 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationCallback.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationCallback.java @@ -9,8 +9,8 @@ import java.util.TreeMap; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.MultiPartEmail; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.components.ExceptionDialog; +import org.corpus_tools.annis.gui.util.Helper; final class MigrationCallback implements FutureCallback { diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationPanel.java b/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationPanel.java index cc57177817..7e0edae417 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationPanel.java @@ -38,16 +38,14 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -import org.corpus_tools.annis.ApiClient; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.api.SearchApi; import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.Background; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.components.ExceptionDialog; import org.corpus_tools.annis.gui.query_references.UrlShortener; +import org.corpus_tools.annis.gui.util.Helper; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.web.reactive.function.client.WebClientResponseException; import org.springframework.web.util.UriComponentsBuilder; public class MigrationPanel extends Panel @@ -94,21 +92,21 @@ public MigrationPanel() { Background.runWithCallback(() -> { try { return migrateUrlShortener(url, username, password, skip, failedQueries); - } catch (ApiException | IOException ex) { + } catch (WebClientResponseException | IOException ex) { ExceptionDialog.show(ex, "Migrating URL shortener table failed", ui); return 0; } }, new MigrationCallback(this, failedQueries)); }); - + emailText.setCaption("E-Mail for status reports (optional)"); emailText.setPlaceholder("you@example.com"); emailText.setWidth(TEXTFIELD_WIDTH, Unit.EM); - formLayout = new FormLayout(exportedFileUpload, serviceUrl, serviceUsername, - servicePassword, skipExisting); + formLayout = new FormLayout(exportedFileUpload, serviceUrl, serviceUsername, servicePassword, + skipExisting); } @Override @@ -147,7 +145,7 @@ public void attach() { migrateButton.setDisableOnClick(true); - Optional user = Helper.getUser(getUI()); + Optional user = Helper.getUser(); if (user.isPresent()) { Set roles = Helper.getUserRoles(user.get()); if (roles.contains("admin")) { @@ -161,15 +159,15 @@ void setMessageAndScrollToEnd(String message) { txtMessages.setCursorPosition(txtMessages.getValue().length() - 1); } - private boolean checkSingleQuery(URLShortenerDefinition q, SearchApi searchApi, - UrlShortener urlShortener, OkHttpClient client, HttpUrl searchServiceBaseUrl, + private boolean checkSingleQuery(URLShortenerDefinition q, UrlShortener urlShortener, + OkHttpClient legacyClient, HttpUrl searchServiceBaseUrl, Multimap failedQueries) { // check the query try { ui.access(() -> progress.setCaption(String.format("testing query %s on corpus %s (UUID %s)", q.getQuery().getQuery().trim(), q.getQuery().getCorpora(), q.getUuid()))); - QueryStatus status = q.test(searchApi, client, searchServiceBaseUrl); + QueryStatus status = q.test(ui.getWebClient(), legacyClient, searchServiceBaseUrl); // insert URLs into new database URI temporary = null; @@ -208,8 +206,8 @@ private String getErrorMessage(Exception ex) { } private boolean readUrlShortenerLine(String[] line, boolean skipExisting, - Set knownCorpora, SearchApi searchApi, OkHttpClient client, - HttpUrl searchServiceBaseUrl, Multimap failedQueries) { + Set knownCorpora, OkHttpClient legacyClient, HttpUrl searchServiceBaseUrl, + Multimap failedQueries) { if (line.length != 4) { return false; @@ -237,7 +235,7 @@ private boolean readUrlShortenerLine(String[] line, boolean skipExisting, failedQueries.put(QueryStatus.UUID_EXISTS, q); } } else { - return checkSingleQuery(q, searchApi, ui.getUrlShortener(), client, searchServiceBaseUrl, + return checkSingleQuery(q, ui.getUrlShortener(), legacyClient, searchServiceBaseUrl, failedQueries); } } @@ -271,15 +269,13 @@ public Response intercept(Chain chain) throws IOException { // test authentication and fail early HttpUrl testUrl = parsedServiceUrl.newBuilder().addPathSegment("annis") .addPathSegment("admin").addPathSegment("is-authenticated").build(); - Request testRequest = - new Request.Builder().url(testUrl).build(); + Request testRequest = new Request.Builder().url(testUrl).build(); try { - String result = - client.build().newCall(testRequest).execute().body().string(); + String result = client.build().newCall(testRequest).execute().body().string(); if (!"true".equalsIgnoreCase(result)) { - ui.access(() -> Notification - .show("Authentication failed, please check the provided user name and password", - Notification.Type.ERROR_MESSAGE)); + ui.access(() -> Notification.show( + "Authentication failed, please check the provided user name and password", + Notification.Type.ERROR_MESSAGE)); return Optional.empty(); } } catch (IOException ex) { @@ -292,7 +288,7 @@ public Response intercept(Chain chain) throws IOException { private int migrateUrlShortener(String serviceURL, String username, String password, boolean skipExisting, Multimap failedQueries) - throws ApiException, IOException { + throws WebClientResponseException, IOException { int successfulQueries = 0; @@ -301,10 +297,9 @@ private int migrateUrlShortener(String serviceURL, String username, String passw HttpUrl searchServiceBaseUrl = parsedServiceUrl.newBuilder().addPathSegment("annis") .addPathSegment("query").addPathSegment("search").build(); - ApiClient apiClient = Helper.getClient(ui); - CorporaApi corporaApi = new CorporaApi(apiClient); - SearchApi searchApi = new SearchApi(apiClient); - Set knownCorpora = new HashSet<>(corporaApi.listCorpora()); + List queriedCorpora = ui.getWebClient().get().uri("/corpora").retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}).block(); + Set knownCorpora = new HashSet<>(queriedCorpora); Optional client = createClient(serviceURL, username, password); @@ -318,8 +313,8 @@ private int migrateUrlShortener(String serviceURL, String username, String passw try (CSVReader csvReader = new CSVReader(new FileReader(urlShortenerFile), '\t')) { String[] line; while ((line = csvReader.readNext()) != null) { - if (readUrlShortenerLine(line, skipExisting, knownCorpora, - searchApi, client.get(), searchServiceBaseUrl, failedQueries)) { + if (readUrlShortenerLine(line, skipExisting, knownCorpora, client.get(), + searchServiceBaseUrl, failedQueries)) { successfulQueries++; } processedQueries++; diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/ReferenceLinkEditor.java b/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/ReferenceLinkEditor.java index 2a871c489f..941c891e16 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/ReferenceLinkEditor.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/ReferenceLinkEditor.java @@ -19,9 +19,9 @@ import java.util.UUID; import java.util.stream.Collectors; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.query_references.UrlShortener; import org.corpus_tools.annis.gui.query_references.UrlShortenerEntry; +import org.corpus_tools.annis.gui.util.Helper; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @@ -171,7 +171,7 @@ public void attach() { } - Optional user = Helper.getUser(getUI()); + Optional user = Helper.getUser(); if (user.isPresent()) { Set roles = Helper.getUserRoles(user.get()); if (roles.contains("admin")) { diff --git a/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/URLShortenerDefinition.java b/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/URLShortenerDefinition.java index b3f79b9060..78ff08b921 100644 --- a/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/URLShortenerDefinition.java +++ b/src/main/java/org/corpus_tools/annis/gui/admin/reflinks/URLShortenerDefinition.java @@ -24,17 +24,15 @@ import okhttp3.Response; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.SearchApi; import org.corpus_tools.annis.api.model.CountExtra; import org.corpus_tools.annis.api.model.CountQuery; import org.corpus_tools.annis.api.model.FindQuery; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.QueryGenerator; import org.corpus_tools.annis.gui.objects.DisplayedResultQuery; import org.corpus_tools.annis.gui.objects.Match; import org.corpus_tools.annis.gui.objects.Query; +import org.corpus_tools.annis.gui.objects.QueryGenerator; import org.corpus_tools.annis.gui.objects.QueryLanguage; +import org.corpus_tools.annis.gui.util.Helper; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; @@ -42,7 +40,13 @@ import org.joda.time.format.DateTimeParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; import org.springframework.web.util.UriComponentsBuilder; +import reactor.core.publisher.Flux; public class URLShortenerDefinition { @@ -181,29 +185,31 @@ public void addUnknownCorpus(String corpus) { unknownCorpora.add(corpus); } - private QueryStatus testFind(SearchApi searchApi, OkHttpClient client, - HttpUrl annisSearchServiceBaseUrl) throws IOException, ApiException { + private QueryStatus testFind(WebClient localClient, OkHttpClient legacyClient, + HttpUrl annisSearchServiceBaseUrl) throws IOException, WebClientResponseException { // Create a file with the matches according to the new graphANNIS based implementation - File matchesGraphANNISFile = searchApi.find(new FindQuery().query(query.getQuery()) - .corpora(new LinkedList<>(query.getCorpora())).queryLanguage(query.getApiQueryLanguage())); + FindQuery findQuery = new FindQuery().query(query.getQuery()) + .corpora(new LinkedList<>(query.getCorpora())).queryLanguage(query.getApiQueryLanguage()); + File findResult = File.createTempFile("annis-find", ".txt"); + Flux matchesGraphANNISFile = localClient.post().uri("/search/find") + .bodyValue(findQuery).retrieve().bodyToFlux(DataBuffer.class); + DataBufferUtils.write(matchesGraphANNISFile, findResult.toPath()); - HttpUrl findUrl = annisSearchServiceBaseUrl.newBuilder().addPathSegment("find") + HttpUrl legacyFindUrl = annisSearchServiceBaseUrl.newBuilder().addPathSegment("find") .addQueryParameter("q", query.getQuery()) .addQueryParameter("corpora", Joiner.on(",").join(query.getCorpora())).build(); Request legacyFindRequest = - new Request.Builder().url(findUrl).addHeader("Accept", "text/plain").build(); + new Request.Builder().url(legacyFindUrl).addHeader("Accept", "text/plain").build(); // read in the file again line by line and compare it with the legacy ANNIS // version int matchNr = 0; - try ( - BufferedReader matchesGraphANNIS = - new BufferedReader(new FileReader(matchesGraphANNISFile)); - BufferedReader matchesLegacy = - new BufferedReader(client.newCall(legacyFindRequest).execute().body().charStream())) { + try (BufferedReader matchesGraphANNIS = new BufferedReader(new FileReader(findResult)); + BufferedReader matchesLegacy = new BufferedReader( + legacyClient.newCall(legacyFindRequest).execute().body().charStream())) { // compare each line String m1; String m2; @@ -221,7 +227,7 @@ private QueryStatus testFind(SearchApi searchApi, OkHttpClient client, } } } finally { - Files.delete(matchesGraphANNISFile.toPath()); + Files.delete(findResult.toPath()); } return QueryStatus.OK; @@ -267,10 +273,11 @@ private int getLegacyCount(OkHttpClient client, HttpUrl annisSearchServiceBaseUr return 0; } - private QueryStatus testQuirksMode(SearchApi searchApi, OkHttpClient client, + private QueryStatus testQuirksMode(WebClient localClient, OkHttpClient remoteClient, HttpUrl annisSearchServiceBaseUrl) { URLShortenerDefinition quirksQuery = this.rewriteInQuirksMode(); - QueryStatus quirksStatus = quirksQuery.test(searchApi, client, annisSearchServiceBaseUrl); + QueryStatus quirksStatus = + quirksQuery.test(localClient, remoteClient, annisSearchServiceBaseUrl); if (quirksStatus == QueryStatus.OK) { this.query = quirksQuery.query; this.uri = quirksQuery.uri; @@ -283,14 +290,17 @@ private QueryStatus testQuirksMode(SearchApi searchApi, OkHttpClient client, } - private QueryStatus testCountAndFind(SearchApi searchApi, OkHttpClient client, - HttpUrl annisSearchServiceBaseUrl) throws IOException, ApiException { + private QueryStatus testCountAndFind(WebClient localClient, OkHttpClient legacyClient, + HttpUrl annisSearchServiceBaseUrl) throws IOException, WebClientResponseException { try { // check count first (also warmup for the corpus) - int countLegacy = getLegacyCount(client, annisSearchServiceBaseUrl); - int countGraphANNIS = searchApi.count(new CountQuery().query(query.getQuery()) - .queryLanguage(query.getApiQueryLanguage()).corpora(new LinkedList<>(query.getCorpora()))) - .getMatchCount(); + int countLegacy = getLegacyCount(legacyClient, annisSearchServiceBaseUrl); + int countGraphANNIS = + localClient.post().uri("/search/count") + .bodyValue(new CountQuery().query(query.getQuery()) + .queryLanguage(query.getApiQueryLanguage()) + .corpora(new LinkedList<>(query.getCorpora()))) + .retrieve().bodyToMono(CountExtra.class).block().getMatchCount(); if (countGraphANNIS != countLegacy) { this.errorMsg = "should have been " + countLegacy + " but was " + countGraphANNIS; return QueryStatus.COUNT_DIFFERS; @@ -298,10 +308,11 @@ private QueryStatus testCountAndFind(SearchApi searchApi, OkHttpClient client, return QueryStatus.OK; } else { // When count is the same and non-empty test if the returned IDs are the same - return testFind(searchApi, client, annisSearchServiceBaseUrl); + return testFind(localClient, legacyClient, annisSearchServiceBaseUrl); } - } catch (ApiException ex) { - if (ex.getCode() == 400 && this.query.getQueryLanguage() == QueryLanguage.AQL) { + } catch (WebClientResponseException ex) { + if (ex.getStatusCode() == HttpStatus.BAD_REQUEST + && this.query.getQueryLanguage() == QueryLanguage.AQL) { // Bad requests means the query was invalid, return error instead of throwing exception this.errorMsg = ex.toString(); return QueryStatus.FAILED; @@ -312,7 +323,7 @@ private QueryStatus testCountAndFind(SearchApi searchApi, OkHttpClient client, } } - public QueryStatus test(SearchApi searchApi, OkHttpClient client, + public QueryStatus test(WebClient localClient, OkHttpClient remoteClient, HttpUrl annisSearchServiceBaseUrl) { if (this.query.getCorpora().isEmpty()) { @@ -321,15 +332,16 @@ public QueryStatus test(SearchApi searchApi, OkHttpClient client, } try { - QueryStatus status = testCountAndFind(searchApi, client, annisSearchServiceBaseUrl); + QueryStatus status = testCountAndFind(localClient, remoteClient, annisSearchServiceBaseUrl); if (status != QueryStatus.OK && this.query.getQueryLanguage() == QueryLanguage.AQL) { // check in quirks mode and rewrite if necessary - status = testQuirksMode(searchApi, client, annisSearchServiceBaseUrl); + status = testQuirksMode(localClient, remoteClient, annisSearchServiceBaseUrl); } return status; - } catch (ApiException ex) { - if (ex.getCode() == 408 || ex.getCode() == 504) { + } catch (WebClientResponseException ex) { + if (ex.getStatusCode() == HttpStatus.REQUEST_TIMEOUT + || ex.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { this.errorMsg = "Timeout in graphANNIS"; return QueryStatus.TIMEOUT; } else { diff --git a/src/main/java/org/corpus_tools/annis/gui/AboutWindow.java b/src/main/java/org/corpus_tools/annis/gui/components/AboutWindow.java similarity index 96% rename from src/main/java/org/corpus_tools/annis/gui/AboutWindow.java rename to src/main/java/org/corpus_tools/annis/gui/components/AboutWindow.java index 5675f98947..dd259d09e6 100644 --- a/src/main/java/org/corpus_tools/annis/gui/AboutWindow.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/AboutWindow.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.components; import com.vaadin.server.ExternalResource; import com.vaadin.server.ThemeResource; @@ -29,6 +29,8 @@ import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; +import org.corpus_tools.annis.gui.util.IDGenerator; +import org.corpus_tools.annis.gui.util.VersionInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/corpus_tools/annis/gui/components/ExampleQueriesPanel.java b/src/main/java/org/corpus_tools/annis/gui/components/ExampleQueriesPanel.java new file mode 100644 index 0000000000..78f78077b0 --- /dev/null +++ b/src/main/java/org/corpus_tools/annis/gui/components/ExampleQueriesPanel.java @@ -0,0 +1,289 @@ +/* + * Copyright 2013 Corpuslinguistic working group Humboldt University Berlin. + * + * 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.corpus_tools.annis.gui.components; + +import com.vaadin.icons.VaadinIcons; +import com.vaadin.server.Resource; +import com.vaadin.shared.ui.ContentMode; +import com.vaadin.ui.Button; +import com.vaadin.ui.Component; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.Column; +import com.vaadin.ui.Grid.SelectionMode; +import com.vaadin.ui.Label; +import com.vaadin.ui.ProgressBar; +import com.vaadin.ui.TabSheet; +import com.vaadin.ui.themes.ValoTheme; +import java.util.HashSet; +import java.util.Set; +import org.corpus_tools.annis.api.model.CorpusConfiguration; +import org.corpus_tools.annis.api.model.ExampleQuery; +import org.corpus_tools.annis.gui.AnnisUI; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.controlpanel.ControlPanel; +import org.corpus_tools.annis.gui.controlpanel.CorpusListPanel; +import org.corpus_tools.annis.gui.controlpanel.QueryPanel; +import org.corpus_tools.annis.gui.objects.Query; +import org.corpus_tools.annis.gui.objects.QueryLanguage; +import org.corpus_tools.annis.gui.resultview.ResultViewPanel; +import org.corpus_tools.annis.gui.util.Helper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Wraps the auto generated queries. + * + * @author Benjamin Weißenfels {@literal } + */ +public class ExampleQueriesPanel extends CssLayout { + + + /** + * + */ + private static final long serialVersionUID = -2676130295297213669L; + + // gets the + private final static Logger log = LoggerFactory.getLogger(ExampleQueriesPanel.class); + + private static final Resource SEARCH_ICON = VaadinIcons.SEARCH; + + /** + * Loads the available example queries for a specific corpus. + * + * @param corpusNames Specifies the corpora example queries are fetched for. If it is null or + * empty all available example queries are fetched. + */ + private static Flux loadExamplesFromRemote(Set corpusNames, CommonUI ui) { + + WebClient client = ui.getWebClient(); + + return Flux.fromIterable(corpusNames).flatMap(corpus -> { + Flux examplesForCorpus = + client.get().uri("/corpora/{corpus}/configuration", corpus).retrieve() + .bodyToMono(CorpusConfiguration.class) + .filter(config -> config.getExampleQueries() != null) + .flatMapIterable(config -> config.getExampleQueries()); + return examplesForCorpus.zipWith(Mono.just(corpus).repeat()); + }).map(t -> { + Entry e = new Entry(); + e.corpus = t.getT2(); + e.example = t.getT1(); + return e; + }); + } + + private final String COLUMN_EXAMPLE_QUERY = "exampleQuery"; + + private final String COLUMN_OPEN_CORPUS_BROWSER = "open corpus browser"; + + private final String COLUMN_DESCRIPTION = "description"; + + // main ui window + private final AnnisUI ui; + + private final Grid table; + + private final ProgressBar loadingIndicator; + + + // reference to the tab which holds this component + private TabSheet.Tab tab; + + // hold the parent tab of annis3 + private final HelpPanel parentTab; + + public static class Entry { + String corpus; + ExampleQuery example; + } + + public ExampleQueriesPanel(AnnisUI ui, HelpPanel parentTab) { + super(); + this.ui = ui; + this.parentTab = parentTab; + + loadingIndicator = new ProgressBar(); + loadingIndicator.setIndeterminate(true); + loadingIndicator.setCaption("Loading example queries..."); + loadingIndicator.setVisible(false); + addComponent(loadingIndicator); + + table = new Grid(); + table.setVisible(true); + addComponent(table); + + setUpTable(); + } + + private Component getOpenCorpusPanel(final String corpusName) { + final Button btn = new Button(corpusName); + + btn.setStyleName(ValoTheme.BUTTON_LINK); + btn.addClickListener(event -> { + CorpusListPanel corpusList = ui.getSearchView().getControlPanel().getCorpusList(); + corpusList.initCorpusBrowser(corpusName, btn, ui); + }); + + return btn; + } + + private void hideTabSheet() { + if (parentTab != null) { + tab = parentTab.getTab(this); + + if (tab != null) { + tab.setEnabled(false); + } + } + } + + /** + * Sets the selected corpora and causes a reload + * + * @param selectedCorpora Specifies the corpora example queries are fetched for. If it is null, + * all available example queries are fetched. + */ + public void setSelectedCorpusInBackground(final Set selectedCorpora) { + loadingIndicator.setVisible(true); + table.setVisible(false); + + + + if (ui instanceof CommonUI) { + loadExamplesFromRemote(selectedCorpora, (CommonUI) ui).collectList() + .subscribe(examples -> ui.access(() -> { + loadingIndicator.setVisible(false); + table.setVisible(true); + + try { + table.setItems(examples); + if (examples.isEmpty()) { + hideTabSheet(); + } else { + showTab(); + } + } catch (Exception ex) { + log.error("removing or adding of example queries failed for {}", selectedCorpora, ex); + } + })); + } + + } + + /** + * Sets some layout properties. + */ + private void setUpTable() { + setSizeFull(); + // expand the table + table.setSizeFull(); + + // Allow selecting items from the table. + table.setSelectionMode(SelectionMode.SINGLE); + + // set custom style + table.addStyleName("example-queries-table"); + + // configure columns + Column corpusBrowserColumn = table.addComponentColumn(e -> { + return getOpenCorpusPanel(e.corpus); + }); + corpusBrowserColumn.setId(COLUMN_OPEN_CORPUS_BROWSER); + corpusBrowserColumn.setCaption("open corpus browser"); + + Column exampleQueryColumn = table.addComponentColumn(e -> { + Button btn = new Button(); + btn.setDescription("show corpus browser for " + e.corpus); + btn.addStyleName(ValoTheme.BUTTON_LINK); + btn.setIcon(SEARCH_ICON); + btn.setCaption(e.example.getQuery()); + btn.setDescription("show results for \"" + e.example.getQuery() + "\" in " + e.corpus); + btn.addStyleName(Helper.CORPUS_FONT_FORCE); + + btn.addClickListener(event -> { + if (ui != null) { + ControlPanel controlPanel = ui.getSearchView().getControlPanel(); + QueryPanel queryPanel; + + if (controlPanel == null) { + log.error("controlPanel is not initialized"); + return; + } + + queryPanel = controlPanel.getQueryPanel(); + if (queryPanel == null) { + log.error("queryPanel is not initialized"); + return; + } + + Set corpusNameSet = new HashSet<>(); + corpusNameSet.add(e.corpus); + if (ui.getQueryController() != null) { + QueryLanguage ql = QueryLanguage.AQL; + if (e.example + .getQueryLanguage() == org.corpus_tools.annis.api.model.QueryLanguage.AQLQUIRKSV3) { + ql = QueryLanguage.AQL_QUIRKS_V3; + } + ui.getQueryController().setQuery(new Query(e.example.getQuery(), ql, corpusNameSet)); + // execute query + ui.getQueryController().executeSearch(true, true); + } + } + }); + return btn; + }); + exampleQueryColumn.setId(COLUMN_EXAMPLE_QUERY); + exampleQueryColumn.setCaption("Example Query"); + + Column descriptionColumn = table.addComponentColumn(e -> { + Label l = new Label(e.example.getDescription()); + l.setContentMode(ContentMode.TEXT); + l.addStyleName(Helper.CORPUS_FONT_FORCE); + return l; + }); + descriptionColumn.setCaption("Description"); + descriptionColumn.setId(COLUMN_DESCRIPTION); + + table.setColumns(COLUMN_EXAMPLE_QUERY, COLUMN_DESCRIPTION, COLUMN_OPEN_CORPUS_BROWSER); + + exampleQueryColumn.setExpandRatio(1); + descriptionColumn.setExpandRatio(1); + + } + + /** + * Shows the tab and put into the foreground, if no query is executed yet. + */ + private void showTab() { + if (parentTab != null) { + tab = parentTab.getTab(this); + if (tab != null) { + // FIXME: this should be added by the constructor or by the panel that adds this + // tab + // tab.getComponent().addStyleName("example-queries-tab"); + tab.setEnabled(true); + + if (!(parentTab.getSelectedTab() instanceof ResultViewPanel)) { + parentTab.setSelectedTab(tab); + } + } + } + + } +} diff --git a/src/main/java/org/corpus_tools/annis/gui/ExampleTable.java b/src/main/java/org/corpus_tools/annis/gui/components/ExampleTable.java similarity index 65% rename from src/main/java/org/corpus_tools/annis/gui/ExampleTable.java rename to src/main/java/org/corpus_tools/annis/gui/components/ExampleTable.java index 8f6252aa66..8cc9f01ac5 100644 --- a/src/main/java/org/corpus_tools/annis/gui/ExampleTable.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/ExampleTable.java @@ -1,8 +1,9 @@ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.components; import com.vaadin.ui.Grid; import com.vaadin.ui.renderers.HtmlRenderer; -import org.corpus_tools.annis.gui.beans.CorpusBrowserEntry; +import org.corpus_tools.annis.gui.corpusbrowser.CorpusBrowserEntry; +import org.corpus_tools.annis.gui.util.Helper; public class ExampleTable extends Grid { @@ -20,9 +21,8 @@ public void attach() { getColumn("name").setCaption("Name"); - Column exampleColumn = addColumn( - cbe -> ("
" + cbe.getExample() + "
"), - new HtmlRenderer()); + Column exampleColumn = addColumn(cbe -> ("
" + cbe.getExample() + "
"), new HtmlRenderer()); exampleColumn.setId("genexample"); exampleColumn.setCaption("Example (click to use query)"); exampleColumn.setExpandRatio(1); diff --git a/src/main/java/org/corpus_tools/annis/gui/components/ExceptionDialog.java b/src/main/java/org/corpus_tools/annis/gui/components/ExceptionDialog.java index 4fe75533e4..ccd328d53a 100644 --- a/src/main/java/org/corpus_tools/annis/gui/components/ExceptionDialog.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/ExceptionDialog.java @@ -28,7 +28,7 @@ import com.vaadin.ui.themes.ValoTheme; import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.CommonUI; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; /** * A dialog that displays the message of an exception and allows to show the diff --git a/src/main/java/org/corpus_tools/annis/gui/ExportPanel.java b/src/main/java/org/corpus_tools/annis/gui/components/ExportPanel.java similarity index 99% rename from src/main/java/org/corpus_tools/annis/gui/ExportPanel.java rename to src/main/java/org/corpus_tools/annis/gui/components/ExportPanel.java index 5f2bf15d8d..34dae6adeb 100644 --- a/src/main/java/org/corpus_tools/annis/gui/ExportPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/ExportPanel.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.components; import com.google.common.base.Joiner; import com.google.common.base.Splitter; @@ -45,9 +45,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.controlpanel.SearchOptionsPanel; import org.corpus_tools.annis.gui.exporter.ExporterPlugin; import org.corpus_tools.annis.gui.objects.QueryUIState; +import org.corpus_tools.annis.gui.util.IDGenerator; import org.slf4j.LoggerFactory; /** diff --git a/src/main/java/org/corpus_tools/annis/gui/components/FrequencyChart.java b/src/main/java/org/corpus_tools/annis/gui/components/FrequencyChart.java index 4c800448b8..7a9d352b9c 100644 --- a/src/main/java/org/corpus_tools/annis/gui/components/FrequencyChart.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/FrequencyChart.java @@ -22,9 +22,9 @@ import java.util.List; import org.corpus_tools.annis.api.model.FrequencyTableRow; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.InstanceConfig; import org.corpus_tools.annis.gui.frequency.FrequencyResultPanel; +import org.corpus_tools.annis.gui.objects.InstanceConfig; +import org.corpus_tools.annis.gui.util.Helper; import org.slf4j.LoggerFactory; /** diff --git a/src/main/java/org/corpus_tools/annis/gui/components/FrequencyWhiteboard.java b/src/main/java/org/corpus_tools/annis/gui/components/FrequencyWhiteboard.java index 244c83c3c0..8f1ee5a7ae 100644 --- a/src/main/java/org/corpus_tools/annis/gui/components/FrequencyWhiteboard.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/FrequencyWhiteboard.java @@ -15,7 +15,7 @@ */ package org.corpus_tools.annis.gui.components; -import static org.corpus_tools.annis.gui.Helper.encodeGeneric; +import static org.corpus_tools.annis.gui.util.Helper.encodeGeneric; import com.vaadin.annotations.JavaScript; import com.vaadin.ui.AbstractJavaScriptComponent; diff --git a/src/main/java/org/corpus_tools/annis/gui/HelpPanel.java b/src/main/java/org/corpus_tools/annis/gui/components/HelpPanel.java similarity index 95% rename from src/main/java/org/corpus_tools/annis/gui/HelpPanel.java rename to src/main/java/org/corpus_tools/annis/gui/components/HelpPanel.java index 6322154772..2eba9445c5 100644 --- a/src/main/java/org/corpus_tools/annis/gui/HelpPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/HelpPanel.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.components; import com.vaadin.server.FontAwesome; import com.vaadin.server.VaadinService; @@ -21,7 +21,8 @@ import com.vaadin.ui.UI; import java.net.URI; import java.net.URISyntaxException; -import org.corpus_tools.annis.gui.components.StatefulBrowserComponent; +import org.corpus_tools.annis.gui.AnnisUI; +import org.corpus_tools.annis.gui.objects.InstanceConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/corpus_tools/annis/gui/HelpUsWindow.java b/src/main/java/org/corpus_tools/annis/gui/components/HelpUsWindow.java similarity index 97% rename from src/main/java/org/corpus_tools/annis/gui/HelpUsWindow.java rename to src/main/java/org/corpus_tools/annis/gui/components/HelpUsWindow.java index 1602afe4d3..2ffe66d891 100644 --- a/src/main/java/org/corpus_tools/annis/gui/HelpUsWindow.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/HelpUsWindow.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.components; import com.vaadin.server.ExternalResource; import com.vaadin.shared.ui.MarginInfo; @@ -27,6 +27,7 @@ import com.vaadin.ui.Window; import com.vaadin.v7.shared.ui.label.ContentMode; import com.vaadin.v7.ui.Label; +import org.corpus_tools.annis.gui.util.IDGenerator; /** * A window displaying an invitation to participate in the development of ANNIS. diff --git a/src/main/java/org/corpus_tools/annis/gui/HistoryPanel.java b/src/main/java/org/corpus_tools/annis/gui/components/HistoryPanel.java similarity index 93% rename from src/main/java/org/corpus_tools/annis/gui/HistoryPanel.java rename to src/main/java/org/corpus_tools/annis/gui/components/HistoryPanel.java index 08e85b10c6..067385f574 100644 --- a/src/main/java/org/corpus_tools/annis/gui/HistoryPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/HistoryPanel.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.components; import com.vaadin.ui.Label; import com.vaadin.ui.Panel; @@ -26,7 +26,10 @@ import com.vaadin.v7.event.ItemClickEvent; import com.vaadin.v7.event.ItemClickEvent.ItemClickListener; import com.vaadin.v7.ui.Table; +import org.corpus_tools.annis.gui.controller.QueryController; import org.corpus_tools.annis.gui.objects.Query; +import org.corpus_tools.annis.gui.query_references.CitationLinkGenerator; +import org.corpus_tools.annis.gui.util.Helper; /** * diff --git a/src/main/java/org/corpus_tools/annis/gui/ImagePanel.java b/src/main/java/org/corpus_tools/annis/gui/components/ImagePanel.java similarity index 96% rename from src/main/java/org/corpus_tools/annis/gui/ImagePanel.java rename to src/main/java/org/corpus_tools/annis/gui/components/ImagePanel.java index 27d02ad97e..01b2c49246 100644 --- a/src/main/java/org/corpus_tools/annis/gui/ImagePanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/ImagePanel.java @@ -1,4 +1,4 @@ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.components; /* * Copyright 2012 Corpuslinguistic working group Humboldt University Berlin. diff --git a/src/main/java/org/corpus_tools/annis/gui/MainToolbar.java b/src/main/java/org/corpus_tools/annis/gui/components/MainToolbar.java similarity index 92% rename from src/main/java/org/corpus_tools/annis/gui/MainToolbar.java rename to src/main/java/org/corpus_tools/annis/gui/components/MainToolbar.java index 35a6e32381..a2fc6e0503 100644 --- a/src/main/java/org/corpus_tools/annis/gui/MainToolbar.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/MainToolbar.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.components; import com.vaadin.server.FontAwesome; import com.vaadin.server.Page; @@ -30,9 +30,17 @@ import com.vaadin.v7.ui.themes.BaseTheme; import java.util.LinkedHashSet; import java.util.Optional; +import org.corpus_tools.annis.gui.AnnisBaseUI; +import org.corpus_tools.annis.gui.AnnisUI; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.SearchView; +import org.corpus_tools.annis.gui.UIConfig; import org.corpus_tools.annis.gui.admin.AdminView; -import org.corpus_tools.annis.gui.components.ScreenshotMaker; -import org.corpus_tools.annis.gui.components.SettingsStorage; +import org.corpus_tools.annis.gui.controller.QueryController; +import org.corpus_tools.annis.gui.objects.SidebarState; +import org.corpus_tools.annis.gui.security.LoginListener; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.annis.gui.util.IDGenerator; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; import org.springframework.security.oauth2.core.user.OAuth2User; @@ -120,7 +128,7 @@ private NavigationTarget(String state, String caption, Resource icon) { private Throwable lastBugReportCause; - private Sidebar sidebar; + private SearchView searchView; private ScreenshotMaker screenshotExtension; @@ -354,15 +362,16 @@ public ScreenshotMaker getScreenshotExtension() { } - public Sidebar getSidebar() { - return sidebar; + public SearchView getSearchView() + { + return searchView; } public boolean isLoggedIn() { - return Helper.getUser(UI.getCurrent()).isPresent(); + return Helper.getUser().isPresent(); } - void notifiyQueryStarted() { + public void notifiyQueryStarted() { if (sidebarState == SidebarState.AUTO_VISIBLE) { sidebarState = SidebarState.AUTO_HIDDEN; } @@ -370,7 +379,7 @@ void notifiyQueryStarted() { updateSidebarState(); } - void onLogin() { + public void onLogin() { for (LoginListener l : loginListeners) { try { l.onLogin(); @@ -409,7 +418,7 @@ private void reportBug() { reportBug(null); } - void reportBug(Throwable cause) { + public void reportBug(Throwable cause) { lastBugReportCause = cause; if (screenshotExtension.isAttached()) { screenshotExtension.makeScreenshot(); @@ -447,7 +456,7 @@ public void setNavigationTarget(NavigationTarget target, UI ui) { if (target == NavigationTarget.ADMIN) { // check in background if display is necessary - updateAdministratorButtonVisibility(Helper.getUser(ui)); + updateAdministratorButtonVisibility(Helper.getUser()); } else if (target != null) { btNavigate.setVisible(true); btNavigate.setCaption(target.caption); @@ -460,9 +469,9 @@ public void setQueryController(QueryController queryController) { this.queryController = queryController; } - public void setSidebar(Sidebar sidebar) { - this.sidebar = sidebar; - btSidebar.setVisible(sidebar != null); + public void setSearchView(SearchView searchView) { + this.searchView = searchView; + btSidebar.setVisible(searchView != null); updateSidebarState(); } @@ -474,9 +483,9 @@ public void showLoginWindow() { } private void updateSidebarState() { - if (sidebar != null && btSidebar != null) { + if (searchView != null && btSidebar != null) { btSidebar.setIcon(sidebarState.getIcon()); - sidebar.updateSidebarState(sidebarState); + searchView.updateSidebarState(sidebarState); } } @@ -503,7 +512,7 @@ private void updateUserInformation() { btNavigate.setVisible(false); } - Optional user = Helper.getUser(UI.getCurrent()); + Optional user = Helper.getUser(); if (user.isPresent()) { // logged in String displayName = Helper.getDisplayName(user.get()); diff --git a/src/main/java/org/corpus_tools/annis/gui/components/MetaDataPanel.java b/src/main/java/org/corpus_tools/annis/gui/components/MetaDataPanel.java new file mode 100644 index 0000000000..d96edc1bf4 --- /dev/null +++ b/src/main/java/org/corpus_tools/annis/gui/components/MetaDataPanel.java @@ -0,0 +1,319 @@ +/* + * Copyright 2011 Corpuslinguistic working group Humboldt University Berlin. + * + * 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.corpus_tools.annis.gui.components; + +import com.google.common.collect.ComparisonChain; +import com.vaadin.data.ValueProvider; +import com.vaadin.data.provider.ListDataProvider; +import com.vaadin.server.SerializableComparator; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.shared.ui.ContentMode; +import com.vaadin.ui.Accordion; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.Column; +import com.vaadin.ui.Label; +import com.vaadin.ui.Panel; +import com.vaadin.ui.ProgressBar; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import org.corpus_tools.annis.api.model.AnnoKey; +import org.corpus_tools.annis.api.model.Annotation; +import org.corpus_tools.annis.api.model.CorpusConfiguration; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.salt.common.SCorpus; +import org.corpus_tools.salt.common.SCorpusGraph; +import org.corpus_tools.salt.common.SDocument; +import org.corpus_tools.salt.core.SMetaAnnotation; +import org.eclipse.emf.common.util.URI; +import reactor.core.publisher.Mono; + +/** + * Provides all corpus annotations for a corpus or for a specific search result. + * + * // TODO cleanup the toplevelCorpus side effects. + * + * @author Thomas Krause {@literal } + * @author Benjamin Weißenfels {@literal } + */ +public class MetaDataPanel extends Panel { + + private static class CorpusMetadataCallResult { + SCorpusGraph metadata; + CorpusConfiguration config; + } + + private static class ConfiguredSortOrderComparator implements SerializableComparator { + + private static final long serialVersionUID = 1L; + private ArrayList corpusAnnotationOrder; + + public ConfiguredSortOrderComparator(Collection corpusAnnotationOrder) { + if (corpusAnnotationOrder == null) { + this.corpusAnnotationOrder = new ArrayList<>(); + } else { + this.corpusAnnotationOrder = new ArrayList<>(corpusAnnotationOrder); + } + + } + + @Override + public int compare(Annotation o1, Annotation o2) { + + String q1 = Helper.getQName(o1.getKey()); + q1 = q1 == null ? "" : q1; + String q2 = Helper.getQName(o2.getKey()); + q2 = q2 == null ? "" : q2; + + + int pos1 = this.corpusAnnotationOrder.indexOf(q1); + int pos2 = this.corpusAnnotationOrder.indexOf(q2); + + if (pos1 < 0) { + pos1 = this.corpusAnnotationOrder.size(); + } + if (pos2 < 0) { + pos2 = this.corpusAnnotationOrder.size(); + } + return ComparisonChain.start().compare(pos1, pos2).compare(q1, q2).result(); + } + } + + + /** + * + */ + private static final long serialVersionUID = -3607697674053863447L; + + + private VerticalLayout layout; + + private String toplevelCorpusName; + + // this is only set if the metadata panel is called from a specific result. + private Optional documentName; + + // holds the current corpus annotation table, when called from corpus browser + private Grid corpusAnnotationTable = null; + + private final ProgressBar progress = new ProgressBar(); + + /** + * this empty label is currently use for empty metadata list on the left side of the corpusbrowser + */ + private Label emptyLabel = new Label("(no metadata)"); + + public MetaDataPanel(String toplevelCorpusName) { + this(toplevelCorpusName, Optional.empty()); + } + + public MetaDataPanel(String toplevelCorpusName, Optional documentName) { + super("Metadata"); + + this.toplevelCorpusName = toplevelCorpusName; + this.documentName = documentName; + + setSizeFull(); + layout = new VerticalLayout(); + layout.setSizeFull(); + setContent(layout); + + progress.setIndeterminate(true); + progress.setSizeFull(); + + layout.addComponent(progress); + layout.setComponentAlignment(progress, Alignment.MIDDLE_CENTER); + } + + + @Override + public void attach() { + super.attach(); + + final UI ui = getUI(); + + Mono config = + Helper.getCorpusConfig(toplevelCorpusName, ui).doOnError(ex -> { + ui.access(() -> { + layout.removeComponent(progress); + ExceptionDialog.show(ex, "Could not get meta data", getUI()); + }); + }); + + Mono metaData = + Helper.getMetaData(toplevelCorpusName, documentName, ui).doOnError(ex -> { + ui.access(() -> { + layout.removeComponent(progress); + ExceptionDialog.show(ex, "Could not get corpus configuration", getUI()); + }); + }); + + Mono.zip(config, metaData).subscribe(t -> { + + CorpusMetadataCallResult result = new CorpusMetadataCallResult(); + result.config = t.getT1(); + result.metadata = t.getT2(); + ui.access(() -> { + layout.removeComponent(progress); + Accordion accordion = new Accordion(); + accordion.setSizeFull(); + + boolean hasDocument = addDocumentMetadata(result, accordion); + boolean hasCorpus = addCorpusMetadata(result, accordion); + + // set output to none if no metadata are available + if (hasDocument || hasCorpus) { + layout.addComponent(accordion); + } else { + addEmptyLabel(); + } + }); + }); + + } + + private boolean addCorpusMetadata(CorpusMetadataCallResult result, Accordion accordion) { + boolean hasResult = false; + + // Sort the (sub-) corpora so sub-corpora come first + List corpora = new ArrayList<>(result.metadata.getCorpora()); + corpora.sort((c1, c2) -> { + URI u1 = c1.getPath(); + URI u2 = c2.getPath(); + return ComparisonChain.start().compare(u1.segmentCount(), u2.segmentCount()) + .compare(u1.toString(), u2.toString()).result(); + }); + + for (SCorpus c : corpora) { + List corpusAnnos = new ArrayList<>(); + for (SMetaAnnotation metaAnno : c.getMetaAnnotations()) { + Annotation anno = new Annotation(); + AnnoKey key = new AnnoKey(); + key.setNs(metaAnno.getNamespace()); + key.setName(metaAnno.getName()); + anno.setKey(key); + anno.setVal(metaAnno.getValue_STEXT()); + corpusAnnos.add(anno); + } + + + if (!corpusAnnos.isEmpty()) { + + String path = c.getPath().toString(); + if (path.startsWith("salt:/")) { + path = path.substring("salt:/".length()); + } + path = path + " (corpus)"; + accordion.addTab(setupTable(new ListDataProvider<>(corpusAnnos), result.config), path); + hasResult = true; + } + } + return hasResult; + } + + private boolean addDocumentMetadata(CorpusMetadataCallResult result, Accordion accordion) { + boolean hasResult = false; + + // Add all document metadata first, then the corpus metadata + List documents = result.metadata.getDocuments(); + if (documents != null) { + // There should only be one document in the corpus graph, but keeping the code generic + // should not hurt + for (SDocument d : documents) { + List docAnnos = new ArrayList<>(); + for (SMetaAnnotation metaAnno : d.getMetaAnnotations()) { + Annotation anno = new Annotation(); + AnnoKey key = new AnnoKey(); + key.setNs(metaAnno.getNamespace()); + key.setName(metaAnno.getName()); + anno.setKey(key); + anno.setVal(metaAnno.getValue_STEXT()); + docAnnos.add(anno); + } + + if (!docAnnos.isEmpty()) { + String path = d.getName(); + + // In case we are called to only output a corpus, this might have been mapped as + // document. So only add the "document" suffix in case we are sure it is an actual + // corpus. + if (documentName.isPresent()) { + path = path + " (document)"; + } + + accordion.addTab(setupTable(new ListDataProvider<>(docAnnos), result.config), path); + hasResult = true; + } + } + } + return hasResult; + } + + /** + * Places a label in the middle center of the corpus browser panel. + */ + private void addEmptyLabel() { + if (emptyLabel == null) { + emptyLabel = new Label("none"); + } + + if (corpusAnnotationTable != null) { + layout.removeComponent(corpusAnnotationTable); + } + + layout.addComponent(emptyLabel); + + // this has only an effect after adding the component to a parent. Bug by + // vaadin? + emptyLabel.setSizeUndefined(); + + layout.setComponentAlignment(emptyLabel, Alignment.MIDDLE_CENTER); + layout.setExpandRatio(emptyLabel, 1.0f); + } + + + private Grid setupTable(ListDataProvider metaData, + CorpusConfiguration config) { + ValueProvider nameProvider = anno -> Helper.getQName(anno.getKey()); + + + if (config == null) { + metaData.setSortOrder(nameProvider, SortDirection.ASCENDING); + } else { + metaData.setSortComparator( + new ConfiguredSortOrderComparator(config.getView().getCorpusAnnotationOrder())); + } + Grid tblMeta = new Grid<>(Annotation.class); + tblMeta.setDataProvider(metaData); + Column nameColumn = tblMeta.addColumn(nameProvider); + nameColumn.setWidthUndefined(); + nameColumn.setCaption("Name"); + nameColumn.setId("genname"); + Column valueColumn = + tblMeta.addComponentColumn(anno -> new Label(anno.getVal(), ContentMode.HTML)); + valueColumn.setId("genval"); + valueColumn.setCaption("Value"); + + tblMeta.setColumns(nameColumn.getId(), valueColumn.getId()); + + tblMeta.setSizeFull(); + valueColumn.setExpandRatio(1); + return tblMeta; + } + +} diff --git a/src/main/java/org/corpus_tools/annis/gui/paging/PagingComponent.java b/src/main/java/org/corpus_tools/annis/gui/components/PagingComponent.java similarity index 98% rename from src/main/java/org/corpus_tools/annis/gui/paging/PagingComponent.java rename to src/main/java/org/corpus_tools/annis/gui/components/PagingComponent.java index 109ab6413e..cfe529204e 100644 --- a/src/main/java/org/corpus_tools/annis/gui/paging/PagingComponent.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/PagingComponent.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui.paging; +package org.corpus_tools.annis.gui.components; import com.vaadin.event.Action; import com.vaadin.event.ShortcutAction; @@ -35,9 +35,10 @@ import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.lang3.StringEscapeUtils; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.controller.PagingCallback; import org.corpus_tools.annis.gui.query_references.ShareQueryReferenceWindow; import org.corpus_tools.annis.gui.util.ANNISFontIcon; +import org.corpus_tools.annis.gui.util.Helper; import org.slf4j.LoggerFactory; /** diff --git a/src/main/java/org/corpus_tools/annis/gui/ReportBugWindow.java b/src/main/java/org/corpus_tools/annis/gui/components/ReportBugWindow.java similarity index 98% rename from src/main/java/org/corpus_tools/annis/gui/ReportBugWindow.java rename to src/main/java/org/corpus_tools/annis/gui/components/ReportBugWindow.java index 95015b9445..17e76a8e96 100644 --- a/src/main/java/org/corpus_tools/annis/gui/ReportBugWindow.java +++ b/src/main/java/org/corpus_tools/annis/gui/components/ReportBugWindow.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.components; import com.vaadin.annotations.PropertyId; import com.vaadin.server.FontAwesome; @@ -53,6 +53,9 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.MultiPartEmail; +import org.corpus_tools.annis.gui.AnnisUI; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.annis.gui.util.VersionInfo; import org.slf4j.LoggerFactory; /** diff --git a/src/main/java/org/corpus_tools/annis/gui/controller/CorpusGraphMessageConverter.java b/src/main/java/org/corpus_tools/annis/gui/controller/CorpusGraphMessageConverter.java new file mode 100644 index 0000000000..c62bab4bdb --- /dev/null +++ b/src/main/java/org/corpus_tools/annis/gui/controller/CorpusGraphMessageConverter.java @@ -0,0 +1,67 @@ +package org.corpus_tools.annis.gui.controller; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.WritableByteChannel; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import javax.xml.stream.XMLStreamException; +import org.corpus_tools.annis.gui.graphml.CorpusGraphMapper; +import org.corpus_tools.salt.common.SCorpusGraph; +import org.springframework.core.ResolvableType; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.MediaType; +import org.springframework.http.ReactiveHttpInputMessage; +import org.springframework.http.codec.HttpMessageReader; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class CorpusGraphMessageConverter implements HttpMessageReader { + + + @Override + public List getReadableMediaTypes() { + // TODO Auto-generated method stub + return Arrays.asList(MediaType.APPLICATION_XML); + } + + @Override + public boolean canRead(ResolvableType elementType, MediaType mediaType) { + return elementType.isAssignableFrom(SCorpusGraph.class) + && MediaType.APPLICATION_XML.equals(mediaType); + } + + @Override + public Flux read(ResolvableType elementType, ReactiveHttpInputMessage message, + Map hints) { + // We can only read a single SDocumentGraph object from a request + return readMono(elementType, message, hints).flux(); + } + + @Override + public Mono readMono(ResolvableType elementType, ReactiveHttpInputMessage message, + Map hints) { + try { + File graphML = File.createTempFile("annis-subgraph-", ".salt"); + WritableByteChannel fileChannel = + Files.newByteChannel(graphML.toPath(), StandardOpenOption.WRITE); + return DataBufferUtils.write(message.getBody(), fileChannel).map(DataBufferUtils::release) + .then(Mono.just(graphML)).flatMap(f -> { + try { + SCorpusGraph result = CorpusGraphMapper.map(f); + Files.deleteIfExists(f.toPath()); + return Mono.just(result); + } catch (XMLStreamException | IOException ex) { + return Mono.error(ex); + } + }); + + } catch (IOException ex) { + return Mono.error(ex); + } + } + +} diff --git a/src/main/java/org/corpus_tools/annis/gui/controller/CountCallback.java b/src/main/java/org/corpus_tools/annis/gui/controller/CountCallback.java index 02e11d90ab..c63f26a2a6 100644 --- a/src/main/java/org/corpus_tools/annis/gui/controller/CountCallback.java +++ b/src/main/java/org/corpus_tools/annis/gui/controller/CountCallback.java @@ -13,70 +13,49 @@ */ package org.corpus_tools.annis.gui.controller; -import java.util.List; -import java.util.Map; -import org.corpus_tools.annis.ApiCallback; -import org.corpus_tools.annis.ApiException; +import java.util.function.Consumer; import org.corpus_tools.annis.api.model.CountExtra; import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.objects.QueryUIState; import org.corpus_tools.annis.gui.resultview.ResultViewPanel; +import org.reactivestreams.Subscription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * * @author Thomas Krause {@literal } */ -public class CountCallback implements ApiCallback { - private final ResultViewPanel panel; - - private final int pageSize; - - private final AnnisUI ui; - - public CountCallback(ResultViewPanel panel, int pageSize, AnnisUI ui) { - this.panel = panel; - this.pageSize = pageSize; - this.ui = ui; - } - - @Override - public void onFailure(ApiException e, int statusCode, - Map> responseHeaders) { - ui.access(() -> { - ui.getQueryState().getExecutedTasks().remove(QueryUIState.QueryType.COUNT); - ui.getQueryController().reportServiceException(e, true); - }); - - } - - @Override - public void onSuccess(CountExtra result, int statusCode, - Map> responseHeaders) { - ui.access(() -> { - ui.getQueryState().getExecutedTasks().remove(QueryUIState.QueryType.COUNT); - - String documentString = result.getDocumentCount() > 1 ? "documents" : "document"; - String matchesString = result.getMatchCount() > 1 ? "matches" : "match"; - ui.getSearchView().getControlPanel().getQueryPanel() - .setStatus("" + result.getMatchCount() + " " + matchesString + "\nin " - + result.getDocumentCount() + " " + documentString); - if (result.getMatchCount() > 0 && panel != null) { - panel.getPaging().setPageSize(pageSize, false); - panel.setCount(result.getMatchCount()); - } - ui.getSearchView().getControlPanel().getQueryPanel().setCountIndicatorEnabled(false); - }); - } - - @Override - public void onUploadProgress(long bytesWritten, long contentLength, boolean done) { - - } - - @Override - public void onDownloadProgress(long bytesRead, long contentLength, boolean done) { - - - } +public class CountCallback implements Consumer { + + private static final Logger log = LoggerFactory.getLogger(CountCallback.class); + + private final ResultViewPanel panel; + private final int pageSize; + private final AnnisUI ui; + private Subscription subscription; + + public CountCallback(ResultViewPanel panel, int pageSize, AnnisUI ui) { + this.panel = panel; + this.pageSize = pageSize; + this.ui = ui; + } + @Override + public void accept(CountExtra result) { + ui.access(() -> { + ui.getQueryState().getExecutedTasks().remove(QueryUIState.QueryType.COUNT); + + String documentString = result.getDocumentCount() > 1 ? "documents" : "document"; + String matchesString = result.getMatchCount() > 1 ? "matches" : "match"; + ui.getSearchView().getControlPanel().getQueryPanel().setStatus("" + result.getMatchCount() + + " " + matchesString + "\nin " + result.getDocumentCount() + " " + documentString); + if (result.getMatchCount() > 0 && panel != null) { + panel.getPaging().setPageSize(pageSize, false); + panel.setCount(result.getMatchCount()); + } + ui.getSearchView().getControlPanel().getQueryPanel().setCountIndicatorEnabled(false); + }); + + } } diff --git a/src/main/java/org/corpus_tools/annis/gui/controller/DocumentGraphMessageConverter.java b/src/main/java/org/corpus_tools/annis/gui/controller/DocumentGraphMessageConverter.java new file mode 100644 index 0000000000..b1d5e5de1b --- /dev/null +++ b/src/main/java/org/corpus_tools/annis/gui/controller/DocumentGraphMessageConverter.java @@ -0,0 +1,67 @@ +package org.corpus_tools.annis.gui.controller; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.WritableByteChannel; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import javax.xml.stream.XMLStreamException; +import org.corpus_tools.annis.gui.graphml.DocumentGraphMapper; +import org.corpus_tools.salt.common.SDocumentGraph; +import org.springframework.core.ResolvableType; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.MediaType; +import org.springframework.http.ReactiveHttpInputMessage; +import org.springframework.http.codec.HttpMessageReader; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class DocumentGraphMessageConverter implements HttpMessageReader { + + + @Override + public List getReadableMediaTypes() { + // TODO Auto-generated method stub + return Arrays.asList(MediaType.APPLICATION_XML); + } + + @Override + public boolean canRead(ResolvableType elementType, MediaType mediaType) { + return elementType.isAssignableFrom(SDocumentGraph.class) + && MediaType.APPLICATION_XML.equals(mediaType); + } + + @Override + public Flux read(ResolvableType elementType, ReactiveHttpInputMessage message, + Map hints) { + // We can only read a single SDocumentGraph object from a request + return readMono(elementType, message, hints).flux(); + } + + @Override + public Mono readMono(ResolvableType elementType, ReactiveHttpInputMessage message, + Map hints) { + try { + File graphML = File.createTempFile("annis-subgraph-", ".salt"); + WritableByteChannel fileChannel = + Files.newByteChannel(graphML.toPath(), StandardOpenOption.WRITE); + return DataBufferUtils.write(message.getBody(), fileChannel).map(DataBufferUtils::release) + .then(Mono.just(graphML)).flatMap(f -> { + try { + SDocumentGraph result = DocumentGraphMapper.map(f); + Files.deleteIfExists(f.toPath()); + return Mono.just(result); + } catch (XMLStreamException | IOException ex) { + return Mono.error(ex); + } + }); + + } catch (IOException ex) { + return Mono.error(ex); + } + } + +} diff --git a/src/main/java/org/corpus_tools/annis/gui/controller/ExportBackgroundJob.java b/src/main/java/org/corpus_tools/annis/gui/controller/ExportBackgroundJob.java index c631d2abb6..d906892fd9 100644 --- a/src/main/java/org/corpus_tools/annis/gui/controller/ExportBackgroundJob.java +++ b/src/main/java/org/corpus_tools/annis/gui/controller/ExportBackgroundJob.java @@ -15,21 +15,19 @@ */ package org.corpus_tools.annis.gui.controller; +import com.google.common.eventbus.EventBus; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.Callable; - -import com.google.common.eventbus.EventBus; - -import org.corpus_tools.annis.ApiException; import org.corpus_tools.annis.api.model.CorpusConfiguration; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.ExportPanel; +import org.corpus_tools.annis.gui.components.ExportPanel; import org.corpus_tools.annis.gui.exporter.ExporterPlugin; import org.corpus_tools.annis.gui.objects.ExportQuery; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * @@ -84,8 +82,9 @@ public File call() throws Exception { if (panel != null) { panel.showResult(currentTmpFile, exportError); } - if (exportError instanceof ApiException) { - ui.getQueryController().reportServiceException((ApiException) exportError, true); + if (exportError instanceof WebClientResponseException) { + ui.getQueryController() + .reportServiceException((WebClientResponseException) exportError, true); } }); } diff --git a/src/main/java/org/corpus_tools/annis/gui/controller/FrequencyBackgroundJob.java b/src/main/java/org/corpus_tools/annis/gui/controller/FrequencyBackgroundJob.java index 417016f97b..1ba6484aec 100644 --- a/src/main/java/org/corpus_tools/annis/gui/controller/FrequencyBackgroundJob.java +++ b/src/main/java/org/corpus_tools/annis/gui/controller/FrequencyBackgroundJob.java @@ -13,39 +13,33 @@ */ package org.corpus_tools.annis.gui.controller; -import com.vaadin.ui.UI; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; import org.apache.commons.lang3.tuple.Pair; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.SearchApi; import org.corpus_tools.annis.api.model.FrequencyQuery; -import org.corpus_tools.annis.api.model.FrequencyQueryDefinition; +import org.corpus_tools.annis.api.model.FrequencyQueryDefinitionInner; import org.corpus_tools.annis.api.model.FrequencyTableRow; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.QueryController; +import org.corpus_tools.annis.gui.CommonUI; import org.corpus_tools.annis.gui.frequency.FrequencyQueryPanel; import org.corpus_tools.annis.gui.objects.FrequencyTableEntry; import org.corpus_tools.salt.util.SaltUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * * @author Thomas Krause {@literal } */ public class FrequencyBackgroundJob implements Callable> { - private static final Logger log = LoggerFactory.getLogger(FrequencyBackgroundJob.class); - private final UI ui; + private final CommonUI ui; private final QueryController queryController; private final org.corpus_tools.annis.gui.objects.FrequencyQuery query; private final FrequencyQueryPanel panel; - public FrequencyBackgroundJob(UI ui, QueryController queryController, + public FrequencyBackgroundJob(CommonUI ui, QueryController queryController, org.corpus_tools.annis.gui.objects.FrequencyQuery query, FrequencyQueryPanel panel) { this.ui = ui; @@ -63,15 +57,15 @@ public List call() throws Exception { private List loadBeans() { List result = new ArrayList<>(); - SearchApi api = new SearchApi(Helper.getClient(ui)); + try { FrequencyQuery frequencyQuery = new FrequencyQuery(); frequencyQuery.setQuery(query.getQuery()); frequencyQuery.setCorpora(new LinkedList<>(query.getCorpora())); frequencyQuery.setQueryLanguage(query.getApiQueryLanguage()); - List freqDef = new LinkedList(); + List freqDef = new LinkedList(); for (FrequencyTableEntry e : query.getFrequencyDefinition()) { - FrequencyQueryDefinition d = new FrequencyQueryDefinition(); + FrequencyQueryDefinitionInner d = new FrequencyQueryDefinitionInner(); d.setNodeRef(e.getReferencedNode()); switch (e.getType()) { case span: @@ -91,8 +85,9 @@ private List loadBeans() { freqDef.add(d); } frequencyQuery.setDefinition(freqDef); - result = api.frequency(frequencyQuery); - } catch (final ApiException ex) { + result = ui.getWebClient().post().uri("/search/frequency").bodyValue(frequencyQuery) + .retrieve().bodyToFlux(FrequencyTableRow.class).collectList().block(); + } catch (final WebClientResponseException ex) { ui.access(() -> queryController.reportServiceException(ex, true)); } return result; diff --git a/src/main/java/org/corpus_tools/annis/gui/controller/SpecificPagingCallback.java b/src/main/java/org/corpus_tools/annis/gui/controller/PagingCallback.java similarity index 63% rename from src/main/java/org/corpus_tools/annis/gui/controller/SpecificPagingCallback.java rename to src/main/java/org/corpus_tools/annis/gui/controller/PagingCallback.java index 58a0abff7b..8669f0c380 100644 --- a/src/main/java/org/corpus_tools/annis/gui/controller/SpecificPagingCallback.java +++ b/src/main/java/org/corpus_tools/annis/gui/controller/PagingCallback.java @@ -13,50 +13,41 @@ */ package org.corpus_tools.annis.gui.controller; -import java.util.concurrent.Future; - -import org.corpus_tools.annis.api.model.FindQuery; +import com.vaadin.ui.UI; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Background; import org.corpus_tools.annis.gui.SearchView; import org.corpus_tools.annis.gui.objects.DisplayedResultQuery; -import org.corpus_tools.annis.gui.objects.PagedResultQuery; -import org.corpus_tools.annis.gui.objects.QueryUIState; -import org.corpus_tools.annis.gui.paging.PagingCallback; -import org.corpus_tools.annis.gui.resultfetch.ResultFetchJob; import org.corpus_tools.annis.gui.resultview.ResultViewPanel; /** * * @author Thomas Krause {@literal } */ -public class SpecificPagingCallback implements PagingCallback { - /** - * - */ +public class PagingCallback { + private static final long serialVersionUID = 2454609714467964162L; private final ResultViewPanel panel; private final SearchView searchView; - private final AnnisUI ui; + private final QueryController queryController; private final DisplayedResultQuery query; - public SpecificPagingCallback(AnnisUI ui, SearchView searchView, ResultViewPanel panel, + public PagingCallback(QueryController queryController, SearchView searchView, + ResultViewPanel panel, DisplayedResultQuery initialQuery) { this.panel = panel; - this.ui = ui; + this.queryController = queryController; this.searchView = searchView; this.query = initialQuery; } - @Override public void switchPage(long offset, int limit) { query.setOffset(offset); query.setLimit(limit); - ui.getQueryController().setQuery(query); + queryController.setQuery(query); // execute the result query again - updateMatches(ui.getQueryController().getSearchQuery(), panel); + updateMatches(queryController.getSearchQuery(), panel); } @@ -64,9 +55,9 @@ private void updateMatches(DisplayedResultQuery newQuery, ResultViewPanel panel) if (panel != null) { searchView.updateFragment(newQuery); searchView.getControlPanel().getQueryPanel().getPiCount().setVisible(true); - searchView.getControlPanel().getQueryPanel().getPiCount().setEnabled(true); - Future future = Background.run(new ResultFetchJob(newQuery, panel, ui)); - ui.getQueryState().getExecutedTasks().put(QueryUIState.QueryType.FIND, future); + if (UI.getCurrent() instanceof AnnisUI) { + queryController.executeFindSearch(newQuery, panel, (AnnisUI) UI.getCurrent()); + } } } diff --git a/src/main/java/org/corpus_tools/annis/gui/QueryController.java b/src/main/java/org/corpus_tools/annis/gui/controller/QueryController.java similarity index 69% rename from src/main/java/org/corpus_tools/annis/gui/QueryController.java rename to src/main/java/org/corpus_tools/annis/gui/controller/QueryController.java index 71e3587ef8..3221739f84 100644 --- a/src/main/java/org/corpus_tools/annis/gui/QueryController.java +++ b/src/main/java/org/corpus_tools/annis/gui/controller/QueryController.java @@ -11,10 +11,14 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.controller; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Joiner; import com.google.common.eventbus.EventBus; import com.google.common.util.concurrent.FutureCallback; +import com.google.gson.Gson; import com.vaadin.data.Binder; import com.vaadin.data.provider.ListDataProvider; import com.vaadin.server.FontAwesome; @@ -27,6 +31,7 @@ import com.vaadin.v7.data.util.BeanContainer; import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -37,19 +42,22 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Future; -import okhttp3.Call; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.JSON; -import org.corpus_tools.annis.api.SearchApi; +import java.util.stream.Collectors; import org.corpus_tools.annis.api.model.BadRequestError; import org.corpus_tools.annis.api.model.CorpusConfiguration; +import org.corpus_tools.annis.api.model.CountExtra; import org.corpus_tools.annis.api.model.CountQuery; +import org.corpus_tools.annis.api.model.FindQuery; import org.corpus_tools.annis.api.model.QueryAttributeDescription; +import org.corpus_tools.annis.api.model.SubgraphWithContext; +import org.corpus_tools.annis.gui.AnnisUI; +import org.corpus_tools.annis.gui.Background; +import org.corpus_tools.annis.gui.SearchView; import org.corpus_tools.annis.gui.components.ExceptionDialog; -import org.corpus_tools.annis.gui.controller.CountCallback; -import org.corpus_tools.annis.gui.controller.ExportBackgroundJob; -import org.corpus_tools.annis.gui.controller.FrequencyBackgroundJob; -import org.corpus_tools.annis.gui.controller.SpecificPagingCallback; +import org.corpus_tools.annis.gui.components.ExportPanel; +import org.corpus_tools.annis.gui.components.HistoryPanel; +import org.corpus_tools.annis.gui.components.PagingComponent; +import org.corpus_tools.annis.gui.components.codemirror.AqlCodeEditorState.ParseError; import org.corpus_tools.annis.gui.controlpanel.QueryPanel; import org.corpus_tools.annis.gui.exporter.ExporterPlugin; import org.corpus_tools.annis.gui.frequency.FrequencyQueryPanel; @@ -64,16 +72,29 @@ import org.corpus_tools.annis.gui.objects.Match; import org.corpus_tools.annis.gui.objects.PagedResultQuery; import org.corpus_tools.annis.gui.objects.Query; +import org.corpus_tools.annis.gui.objects.QueryGenerator; import org.corpus_tools.annis.gui.objects.QueryLanguage; import org.corpus_tools.annis.gui.objects.QueryUIState; -import org.corpus_tools.annis.gui.resultfetch.ResultFetchJob; -import org.corpus_tools.annis.gui.resultfetch.SingleResultFetchJob; import org.corpus_tools.annis.gui.resultview.ResultViewPanel; import org.corpus_tools.annis.gui.resultview.VisualizerContextChanger; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.IFrameResourceMap; +import org.corpus_tools.salt.SaltFactory; +import org.corpus_tools.salt.common.SCorpusGraph; +import org.corpus_tools.salt.common.SDocument; +import org.corpus_tools.salt.common.SDocumentGraph; import org.corpus_tools.salt.common.SaltProject; +import org.eclipse.emf.common.util.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.function.Tuple3; /** * A controller to modifiy the query UI state. s @@ -158,8 +179,7 @@ public void addHistoryEntry(Query q) { queryCopy = new DisplayedResultQuery((DisplayedResultQuery) q); } else if (q instanceof ContextualizedQuery) { queryCopy = new ContextualizedQuery((ContextualizedQuery) q); - } - else { + } else { queryCopy = new Query(q); } @@ -218,7 +238,6 @@ public void changeContext(DisplayedResultQuery originalQuery, Match match, long newQuery.setOffset(offset); - UI ui = UI.getCurrent(); Background.runWithCallback(new SingleResultFetchJob(match, newQuery, ui), new FutureCallback() { @@ -267,9 +286,8 @@ public void executeExport(ExportPanel panel, EventBus eventBus) { addHistoryEntry(query); - Optional exporterImpl = - ui.getExporterPlugins().stream().filter((e) -> e.getClass().equals(query.getExporter())) - .findAny(); + Optional exporterImpl = ui.getExporterPlugins().stream() + .filter((e) -> e.getClass().equals(query.getExporter())).findAny(); UI ui = UI.getCurrent(); if (exporterImpl.isPresent() && ui instanceof AnnisUI) { @@ -313,7 +331,6 @@ public void executeFrequency(FrequencyQueryPanel panel) { addHistoryEntry(query); - UI ui = UI.getCurrent(); FrequencyBackgroundJob job = new FrequencyBackgroundJob(ui, this, query, panel); freqFuture = Background.call(job); @@ -382,9 +399,6 @@ public void executeSearch(boolean replaceOldTab, boolean freshQuery) { addHistoryEntry(displayedQuery); - // - // begin execute match fetching - // ResultViewPanel oldPanel = searchView.getLastSelectedResultView(); if (replaceOldTab) { // remove old panel from view @@ -394,10 +408,9 @@ public void executeSearch(boolean replaceOldTab, boolean freshQuery) { if (ui instanceof AnnisUI) { AnnisUI annisUI = (AnnisUI) ui; ResultViewPanel newResultView = - new ResultViewPanel(annisUI, displayedQuery); + new ResultViewPanel(displayedQuery, annisUI.getQueryController()); newResultView.getPaging() - .addCallback( - new SpecificPagingCallback(annisUI, searchView, newResultView, displayedQuery)); + .addCallback(new PagingCallback(this, searchView, newResultView, displayedQuery)); TabSheet.Tab newTab; @@ -411,31 +424,139 @@ public void executeSearch(boolean replaceOldTab, boolean freshQuery) { searchView.getMainTab().setSelectedTab(newResultView); searchView.notifiyQueryStarted(); + executeFindSearch(displayedQuery, newResultView, annisUI); + fetchCountResults(displayedQuery, newResultView, annisUI); + } + } + + public void executeFindSearch(PagedResultQuery query, ResultViewPanel resultPanel, AnnisUI ui) { + resultPanel.showMatchSearchInProgress(query.getSegmentation()); + + FindQuery q = new FindQuery(); + q.setCorpora(new LinkedList<>(query.getCorpora())); + q.setQuery(query.getQuery()); + q.setOffset((int) query.getOffset()); + q.setLimit(query.getLimit()); + q.setQueryLanguage(query.getApiQueryLanguage()); + q.setOrder(query.getOrder()); + + Mono> corpusConfigMap = + Helper.getCorpusConfigurationMap(q.getCorpora(), ui.getWebClient()); + + // The default string decoder will split the result line by line + Flux matches = ui.getWebClient().post().uri("/search/find") + .contentType(MediaType.APPLICATION_JSON).bodyValue(q).accept(MediaType.TEXT_PLAIN) + .retrieve().bodyToFlux(String.class).map(Match::parseFromString); + // Get the subgraph for each match + Flux>> subgraphs = + matches.flatMap(m -> { + SubgraphWithContext arg = new SubgraphWithContext(); + arg.setLeft(query.getLeftContext()); + arg.setRight(query.getRightContext()); + arg.setSegmentation(query.getSegmentation()); + arg.setNodeIds(m.getSaltIDs().stream().collect(Collectors.toList())); + return Mono.zip(createSaltFromMatch(m, arg), Mono.just(m), corpusConfigMap); + }); - Background.run(new ResultFetchJob(displayedQuery, newResultView, annisUI)); - - // - // end execute match fetching - // - // - // begin execute count - // - // start count query - searchView.getControlPanel().getQueryPanel().setCountIndicatorEnabled(true); - - SearchApi api = new SearchApi(Helper.getClient(ui)); - CountQuery countQuery = new CountQuery(); - countQuery.setCorpora(new LinkedList<>(displayedQuery.getCorpora())); - countQuery.setQuery(displayedQuery.getQuery()); - countQuery.setQueryLanguage(displayedQuery.getApiQueryLanguage()); - try { - Call call = api.countAsync(countQuery, - new CountCallback(newResultView, displayedQuery.getLimit(), annisUI)); - - state.getExecutedCalls().put(QueryUIState.QueryType.COUNT, call); - } catch (ApiException ex) { - ExceptionDialog.show(ex, ui); + subgraphs.doOnError(unknownException -> { + + log.error("Could not execute find query", unknownException); + if (unknownException instanceof WebClientResponseException) { + WebClientResponseException ex = (WebClientResponseException) unknownException; + ui.access(() -> { + if (resultPanel != null && resultPanel.getPaging() != null) { + PagingComponent paging = resultPanel.getPaging(); + + if (ex.getStatusCode() == HttpStatus.BAD_REQUEST) { + Gson json = new Gson(); + BadRequestError error = + json.fromJson(ex.getResponseBodyAsString(), BadRequestError.class); + + String errMsg = ""; + if (error.getAqLSyntaxError() != null) { + errMsg = new ParseError(error.getAqLSyntaxError()).message; + } + if (error.getAqLSemanticError() != null) { + errMsg = new ParseError(error.getAqLSemanticError()).message; + } + + paging.setInfo("parsing error: " + errMsg); + } else if (ex.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { + paging.setInfo("Timeout: query execution took too long"); + } else if (ex.getStatusCode() == HttpStatus.FORBIDDEN) { + paging.setInfo("Not authorized to query this corpus."); + } else { + ExceptionDialog.show(ex, ui); + } + resultPanel.showFinishedSubgraphSearch(); + + } + }); } + }).subscribe(tuple -> ui.accessSynchronously(() -> resultPanel.addQueryResult(query, + tuple.getT1(), Arrays.asList(tuple.getT2()), tuple.getT3(), ui))); + + } + + private Mono createSaltFromMatch(Match m, SubgraphWithContext arg) { + List corpusPathRaw = Helper.getCorpusPath(m.getSaltIDs().get(0), false); + List corpusPathDecoded = Helper.getCorpusPath(m.getSaltIDs().get(0), true); + + SaltProject p = SaltFactory.createSaltProject(); + final SCorpusGraph cg = p.createCorpusGraph(); + + if (corpusPathRaw.size() == 1) { + // This match describes a corpus + cg.createCorpus(null, corpusPathRaw.get(0)); + } else if (corpusPathRaw.size() > 1) { + // Get the document graph via a REST API call + Mono docGraph = + ui.getWebClient().post().uri("/corpora/{corpus}/subgraph", corpusPathDecoded.get(0)) + .contentType(MediaType.APPLICATION_JSON).bodyValue(arg) + .accept(MediaType.APPLICATION_XML).retrieve().bodyToMono(SDocumentGraph.class); + // Add the received document graph to the Salt project + Mono project = docGraph.flatMap(d -> { + URI docURI = URI.createURI("salt:/" + Joiner.on('/').join(corpusPathRaw)); + SDocument doc = cg.createDocument(docURI); + doc.setDocumentGraph(d); + Helper.addMatchToDocumentGraph(m, doc.getDocumentGraph()); + return Mono.just(p); + }); + return project; + } + return Mono.just(p); + } + + private void fetchCountResults(DisplayedResultQuery displayedQuery, ResultViewPanel resultPanel, + AnnisUI ui) { + + searchView.getControlPanel().getQueryPanel().setCountIndicatorEnabled(true); + + CountQuery countQuery = new CountQuery(); + countQuery.setCorpora(new LinkedList<>(displayedQuery.getCorpora())); + countQuery.setQuery(displayedQuery.getQuery()); + countQuery.setQueryLanguage(displayedQuery.getApiQueryLanguage()); + + WebClient client = ((AnnisUI) ui).getWebClient(); + + try { + CountCallback callback = new CountCallback(resultPanel, displayedQuery.getLimit(), ui); + Disposable countDisposable = client.post().uri("/search/count").bodyValue(countQuery) + .retrieve().bodyToMono(CountExtra.class).doOnNext(callback).doOnError(t -> { + ui.access(() -> { + ui.getQueryState().getExecutedTasks().remove(QueryUIState.QueryType.COUNT); + if (t instanceof WebClientResponseException) { + ui.getQueryController().reportServiceException((WebClientResponseException) t, + true); + } else { + log.error("Could not get count result", t); + } + }); + }).single().subscribe(); + + state.getExecutedCalls().put(QueryUIState.QueryType.COUNT, countDisposable); + } catch (WebClientResponseException ex) { + ExceptionDialog.show(ex, ui); } } @@ -449,10 +570,8 @@ public ExportQuery getExportQuery() { .corpora(new LinkedHashSet<>(state.getSelectedCorpora())) .queryLanguage(state.getQueryLanguageLegacy()).left(state.getLeftContext()) .right(state.getRightContext()).segmentation(state.getVisibleBaseText().getValue()) - .exporter(state.getExporter()) - .annotations(state.getExportAnnotationKeys()) - .param(state.getExportParameters()).alignmc(state.getAlignmc().getValue()) - .build(); + .exporter(state.getExporter()).annotations(state.getExportAnnotationKeys()) + .param(state.getExportParameters()).alignmc(state.getAlignmc().getValue()).build(); } private List getResultPanels() { @@ -492,17 +611,25 @@ public QueryUIState getState() { * @param showNotification If true a notification is shown instead of only displaying the error in * the status label. */ - public void reportServiceException(ApiException ex, boolean showNotification) { + public void reportServiceException(WebClientResponseException ex, boolean showNotification) { QueryPanel qp = searchView.getControlPanel().getQueryPanel(); String caption = null; String description = null; if (!ui.handleCommonError(ex, "execute query")) { - switch (ex.getCode()) { - case 400: - BadRequestError error = - JSON.createGson().create().fromJson(ex.getResponseBody(), BadRequestError.class); + switch (ex.getStatusCode()) { + case BAD_REQUEST: + + ObjectMapper mapper = new ObjectMapper(); + BadRequestError error = new BadRequestError(); + try { + error = mapper.readValue(ex.getResponseBodyAsString(), BadRequestError.class); + } catch (JsonProcessingException parseEx) { + ui.access(() -> { + ExceptionDialog.show(parseEx, "Could not parse response from server", ui); + }); + } caption = "Parsing error"; if (error.getAqLSyntaxError() != null) { @@ -517,13 +644,13 @@ public void reportServiceException(ApiException ex, boolean showNotification) { qp.setError(error); qp.setStatus(description); break; - case 504: + case GATEWAY_TIMEOUT: caption = "Timeout"; description = "Query execution took too long."; qp.setStatus(caption + ": " + description); break; - case 403: - if (!Helper.getUser(ui.getSecurityContext()).isPresent()) { + case FORBIDDEN: + if (!Helper.getUser().isPresent()) { // not logged in qp.setStatus("You don't have the access rights to query this corpus. " + "You might want to login to access more corpora."); @@ -620,36 +747,14 @@ public void validateQuery() { String query = state.getAql().getValue(); if (query == null || query.isEmpty()) { qp.setStatus("Empty query"); - } else { // validate query - UI ui = UI.getCurrent(); - Background.runWithCallback(() -> { - SearchApi api = new SearchApi(Helper.getClient(ui)); - return api.nodeDescriptions(query, org.corpus_tools.annis.api.model.QueryLanguage.AQL); - }, new FutureCallback>() { - - @Override - public void onSuccess(List nodes) { - qp.setNodes(nodes); - - if (state.getSelectedCorpora() == null || state.getSelectedCorpora().isEmpty()) { - qp.setStatus("Please select a corpus from the list below, then click on \"Search\"."); - } else { - qp.setStatus("Valid query, click on \"Search\" to start searching."); - } - - } - - @Override - public void onFailure(Throwable t) { - if (t instanceof ApiException) { - reportServiceException((ApiException) t, false); - } - } - - }); + WebClient client = ui.getWebClient(); + client.get() + .uri(ub -> ub.path("/search/node-descriptions").queryParam("query", query).build()) + .retrieve().bodyToFlux(QueryAttributeDescription.class) + .subscribe(new ValidateCallback(qp, this, ui)); } } diff --git a/src/main/java/org/corpus_tools/annis/gui/resultfetch/ResultFetchJob.java b/src/main/java/org/corpus_tools/annis/gui/controller/ResultFetchJob.java similarity index 75% rename from src/main/java/org/corpus_tools/annis/gui/resultfetch/ResultFetchJob.java rename to src/main/java/org/corpus_tools/annis/gui/controller/ResultFetchJob.java index 81eb2f80f6..a6c5aecf74 100644 --- a/src/main/java/org/corpus_tools/annis/gui/resultfetch/ResultFetchJob.java +++ b/src/main/java/org/corpus_tools/annis/gui/controller/ResultFetchJob.java @@ -11,9 +11,10 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui.resultfetch; +package org.corpus_tools.annis.gui.controller; import com.google.common.base.Joiner; +import com.google.gson.Gson; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -24,23 +25,19 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.xml.stream.XMLStreamException; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.JSON; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.api.SearchApi; import org.corpus_tools.annis.api.model.BadRequestError; import org.corpus_tools.annis.api.model.FindQuery; import org.corpus_tools.annis.api.model.SubgraphWithContext; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.components.ExceptionDialog; +import org.corpus_tools.annis.gui.components.PagingComponent; import org.corpus_tools.annis.gui.components.codemirror.AqlCodeEditorState.ParseError; import org.corpus_tools.annis.gui.graphml.DocumentGraphMapper; import org.corpus_tools.annis.gui.objects.Match; import org.corpus_tools.annis.gui.objects.MatchGroup; import org.corpus_tools.annis.gui.objects.PagedResultQuery; -import org.corpus_tools.annis.gui.paging.PagingComponent; import org.corpus_tools.annis.gui.resultview.ResultViewPanel; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.SaltFactory; import org.corpus_tools.salt.common.SCorpusGraph; import org.corpus_tools.salt.common.SDocument; @@ -49,6 +46,12 @@ import org.eclipse.emf.common.util.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Flux; /** * A thread that queries for the matches, fetches the the subgraph for the matches and updates the @@ -75,9 +78,6 @@ public ResultFetchJob(PagedResultQuery query, ResultViewPanel resultPanel, Annis @Override public void run() { - SearchApi search = new SearchApi(Helper.getClient(ui)); - CorporaApi corpora = new CorporaApi(Helper.getClient(ui)); - // holds the ids of the matches. MatchGroup result = new MatchGroup(); @@ -98,7 +98,16 @@ public void run() { q.setLimit(query.getLimit()); q.setQueryLanguage(query.getApiQueryLanguage()); q.setOrder(query.getOrder()); - File findResult = search.find(q); + + + Flux response = + ui.getWebClient().post().uri("/search/find") + .contentType(MediaType.APPLICATION_JSON).bodyValue(q).accept(MediaType.TEXT_PLAIN) + .retrieve().bodyToFlux(DataBuffer.class); + + File findResult = File.createTempFile("annis-result", ".txt"); + DataBufferUtils.write(response, findResult.toPath()).block(); + try (Stream findResultLines = Files.lines(findResult.toPath(), StandardCharsets.UTF_8)) { findResultLines.forEachOrdered(line -> { @@ -106,6 +115,8 @@ public void run() { result.getMatches().add(m); }); } + Files.deleteIfExists(findResult.toPath()); + // get the subgraph for each match, when the result is not empty if (result.getMatches().isEmpty()) { @@ -138,8 +149,9 @@ public void run() { arg.setSegmentation(query.getSegmentation()); arg.setNodeIds(m.getSaltIDs().stream().collect(Collectors.toList())); - SaltProject p = createSaltFromMatch(m, arg, current, corpora); - ui.access(() -> resultPanel.addQueryResult(query, p, matchList)); + SaltProject p = createSaltFromMatch(m, arg, current); + ui.access(() -> resultPanel.addQueryResult(query, p, matchList, + Helper.getCorpusConfigurationMap(q.getCorpora(), ui.getWebClient()).block(), ui)); if (Thread.interrupted()) { @@ -150,14 +162,16 @@ public void run() { } } // end if no results - } catch (final ApiException ex) { + } catch (final WebClientResponseException ex) { + log.error("Could not execute find query", ex); ui.access(() -> { if (resultPanel != null && resultPanel.getPaging() != null) { PagingComponent paging = resultPanel.getPaging(); - if (ex.getCode() == 400) { - JSON json = new JSON(); - BadRequestError error = json.deserialize(ex.getResponseBody(), BadRequestError.class); + if (ex.getStatusCode() == HttpStatus.BAD_REQUEST) { + Gson json = new Gson(); + BadRequestError error = + json.fromJson(ex.getResponseBodyAsString(), BadRequestError.class); String errMsg = ""; if (error.getAqLSyntaxError() != null) { @@ -168,9 +182,9 @@ public void run() { } paging.setInfo("parsing error: " + errMsg); - } else if (ex.getCode() == 504) { + } else if (ex.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) { paging.setInfo("Timeout: query execution took too long"); - } else if (ex.getCode() == 403) { + } else if (ex.getStatusCode() == HttpStatus.FORBIDDEN) { paging.setInfo("Not authorized to query this corpus."); } else { ExceptionDialog.show(ex, ui); @@ -179,19 +193,27 @@ public void run() { } }); - } catch(IOException ex) { + } catch (IOException ex) { ui.access(() -> ExceptionDialog.show(ex, ui)); } } - private SaltProject createSaltFromMatch(Match m, SubgraphWithContext arg, int currentMatchNumber, - CorporaApi api) throws ApiException { + private SaltProject createSaltFromMatch(Match m, SubgraphWithContext arg, int currentMatchNumber) throws WebClientResponseException, IOException { List corpusPathRaw = Helper.getCorpusPath(m.getSaltIDs().get(0), false); List corpusPathDecoded = Helper.getCorpusPath(m.getSaltIDs().get(0), true); final SaltProject p = SaltFactory.createSaltProject(); if (!corpusPathRaw.isEmpty()) { - File graphML = api.subgraphForNodes(corpusPathDecoded.get(0), arg); + Flux response = + ui.getWebClient().post() + .uri("/corpora/{corpus}/subgraph", + corpusPathDecoded.get(0)) + .contentType(MediaType.APPLICATION_JSON).bodyValue(arg) + .accept(MediaType.APPLICATION_XML) + .retrieve().bodyToFlux(DataBuffer.class); + File graphML = File.createTempFile("annis-subgraph", ".salt"); + DataBufferUtils.write(response, graphML.toPath()).block(); + try { final SCorpusGraph cg = p.createCorpusGraph(); @@ -199,10 +221,7 @@ private SaltProject createSaltFromMatch(Match m, SubgraphWithContext arg, int cu if (corpusPathRaw.size() > 1) { SDocument doc = cg.createDocument(docURI); SDocumentGraph docGraph = DocumentGraphMapper.map(graphML); - if (Files.deleteIfExists(graphML.toPath())) { - log.debug("Could not delete temporary SaltXML file {} because it does not exist.", - graphML.getPath()); - } + Files.deleteIfExists(graphML.toPath()); doc.setDocumentGraph(docGraph); Helper.addMatchToDocumentGraph(m, doc.getDocumentGraph()); } else if (corpusPathRaw.size() == 1) { diff --git a/src/main/java/org/corpus_tools/annis/gui/resultfetch/SingleResultFetchJob.java b/src/main/java/org/corpus_tools/annis/gui/controller/SingleResultFetchJob.java similarity index 77% rename from src/main/java/org/corpus_tools/annis/gui/resultfetch/SingleResultFetchJob.java rename to src/main/java/org/corpus_tools/annis/gui/controller/SingleResultFetchJob.java index 6dc3b5bf67..3eddb136d7 100644 --- a/src/main/java/org/corpus_tools/annis/gui/resultfetch/SingleResultFetchJob.java +++ b/src/main/java/org/corpus_tools/annis/gui/controller/SingleResultFetchJob.java @@ -11,20 +11,19 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui.resultfetch; +package org.corpus_tools.annis.gui.controller; import com.google.common.base.Joiner; -import com.vaadin.ui.UI; import java.io.File; import java.nio.file.Files; import java.util.List; import java.util.concurrent.Callable; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.api.model.SubgraphWithContext; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.CommonUI; import org.corpus_tools.annis.gui.graphml.DocumentGraphMapper; import org.corpus_tools.annis.gui.objects.Match; import org.corpus_tools.annis.gui.objects.PagedResultQuery; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.SaltFactory; import org.corpus_tools.salt.common.SCorpusGraph; import org.corpus_tools.salt.common.SDocument; @@ -33,6 +32,11 @@ import org.eclipse.emf.common.util.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; /** * Fetches a result which contains only one subgraph. This single query always follows a normal @@ -53,9 +57,9 @@ public class SingleResultFetchJob implements Callable private final PagedResultQuery query; - private final UI ui; + private final CommonUI ui; - public SingleResultFetchJob(Match match, PagedResultQuery query, UI ui) { + public SingleResultFetchJob(Match match, PagedResultQuery query, CommonUI ui) { this.match = match; this.query = query; this.ui = ui; @@ -63,7 +67,7 @@ public SingleResultFetchJob(Match match, PagedResultQuery query, UI ui) { @Override public SaltProject call() throws Exception { - CorporaApi api = new CorporaApi(Helper.getClient(ui)); + WebClient client = ui.getWebClient(); if (Thread.interrupted()) { return null; @@ -81,14 +85,16 @@ public SaltProject call() throws Exception { SCorpusGraph cg = p.createCorpusGraph(); if (!corpusPath.isEmpty()) { - File graphML = api.subgraphForNodes(corpusPath.get(0), subgraphQuery); + File graphML = File.createTempFile("annis-subgraph", ".salt"); + Flux response = client.post() + .uri("/corpora/{corpus}/subgraph", corpusPath.get(0)) + .accept(MediaType.APPLICATION_XML).bodyValue(subgraphQuery).retrieve() + .bodyToFlux(DataBuffer.class); + DataBufferUtils.write(response, graphML.toPath()).block(); URI docURI = URI.createURI("salt:/" + Joiner.on('/').join(corpusPath)); SDocument doc = cg.createDocument(docURI); SDocumentGraph docGraph = DocumentGraphMapper.map(graphML); - if (Files.deleteIfExists(graphML.toPath())) { - log.debug("Could not delete temporary SaltXML file {} because it does not exist.", - graphML.getPath()); - } + Files.deleteIfExists(graphML.toPath()); doc.setDocumentGraph(docGraph); Helper.addMatchToDocumentGraph(match, doc.getDocumentGraph()); } diff --git a/src/main/java/org/corpus_tools/annis/gui/controller/ValidateCallback.java b/src/main/java/org/corpus_tools/annis/gui/controller/ValidateCallback.java new file mode 100644 index 0000000000..d53de6cd30 --- /dev/null +++ b/src/main/java/org/corpus_tools/annis/gui/controller/ValidateCallback.java @@ -0,0 +1,58 @@ +package org.corpus_tools.annis.gui.controller; + +import com.vaadin.ui.UI; +import java.util.LinkedList; +import java.util.List; +import org.corpus_tools.annis.api.model.QueryAttributeDescription; +import org.corpus_tools.annis.gui.controlpanel.QueryPanel; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +public class ValidateCallback implements Subscriber { + + private List result; + + private final UI ui; + private final QueryPanel qp; + private final QueryController controller; + + public ValidateCallback(QueryPanel qp, QueryController controller, UI ui) { + this.qp = qp; + this.controller = controller; + this.ui = ui; + } + + @Override + public void onSubscribe(Subscription s) { + result = new LinkedList<>(); + } + + @Override + public void onNext(QueryAttributeDescription attDesc) { + result.add(attDesc); + + } + + @Override + public void onError(Throwable t) { + if (t instanceof WebClientResponseException) { + controller.reportServiceException((WebClientResponseException) t, false); + } + } + + @Override + public void onComplete() { + + ui.access(() -> { + qp.setNodes(result); + if (controller.getState().getSelectedCorpora() == null + || controller.getState().getSelectedCorpora().isEmpty()) { + qp.setStatus("Please select a corpus from the list below, then click on \"Search\"."); + } else { + qp.setStatus("Valid query, click on \"Search\" to start searching."); + } + }); + } + +} diff --git a/src/main/java/org/corpus_tools/annis/gui/controlpanel/ControlPanel.java b/src/main/java/org/corpus_tools/annis/gui/controlpanel/ControlPanel.java index 2faa47ca56..98f04cbf0e 100644 --- a/src/main/java/org/corpus_tools/annis/gui/controlpanel/ControlPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/controlpanel/ControlPanel.java @@ -29,8 +29,6 @@ public class ControlPanel extends VerticalLayout { private static final long serialVersionUID = -2220211539424865671L; - private final AnnisUI ui; - private final QueryPanel queryPanel; private final CorpusListPanel corpusList; @@ -40,7 +38,7 @@ public class ControlPanel extends VerticalLayout { private final TabSheet optionsTab; public ControlPanel(AnnisUI ui) { - this.ui = ui; + setSizeFull(); setMargin(true); @@ -52,7 +50,7 @@ public ControlPanel(AnnisUI ui) { optionsTab.addStyleName(ValoTheme.TABSHEET_FRAMED); queryPanel = new QueryPanel(ui); - corpusList = new CorpusListPanel(ui); + corpusList = new CorpusListPanel(); searchOptions = new SearchOptionsPanel(); optionsTab.addTab(corpusList, "Corpus List", null); diff --git a/src/main/java/org/corpus_tools/annis/gui/controlpanel/CorpusListPanel.java b/src/main/java/org/corpus_tools/annis/gui/controlpanel/CorpusListPanel.java index 3e464925b9..074bf0443c 100644 --- a/src/main/java/org/corpus_tools/annis/gui/controlpanel/CorpusListPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/controlpanel/CorpusListPanel.java @@ -42,18 +42,17 @@ import java.util.List; import java.util.TreeSet; import java.util.stream.Collectors; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Background; -import org.corpus_tools.annis.gui.CorpusBrowserPanel; -import org.corpus_tools.annis.gui.CorpusSet; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.IDGenerator; -import org.corpus_tools.annis.gui.MetaDataPanel; import org.corpus_tools.annis.gui.components.ExceptionDialog; +import org.corpus_tools.annis.gui.components.MetaDataPanel; +import org.corpus_tools.annis.gui.corpusbrowser.CorpusBrowserPanel; +import org.corpus_tools.annis.gui.objects.CorpusSet; import org.corpus_tools.annis.gui.objects.QueryUIState; -import org.slf4j.LoggerFactory; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.annis.gui.util.IDGenerator; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.reactive.function.client.WebClientResponseException; import org.vaadin.extension.gridscroll.GridScrollExtension; /** @@ -65,52 +64,8 @@ public class CorpusListPanel extends VerticalLayout { private static final String COMPACT_COLUMN_CLASS = "compact-column"; - private class CorpusListUpdater implements Runnable { - - private final boolean showLoginMessage; - - public CorpusListUpdater(boolean showLoginMessage) { - this.showLoginMessage = showLoginMessage; - } - - @Override - public void run() { - - try { - // query in background - CorporaApi api = new CorporaApi(Helper.getClient(ui)); - - List corpora = api.listCorpora(); - - // update the GUI - ui.access(() -> { - availableCorpora = new ListDataProvider<>(corpora); - availableCorpora.setFilter(filter); - HashSet oldSelectedItems = new HashSet<>(tblCorpora.getSelectedItems()); - tblCorpora.setDataProvider(availableCorpora); - // reset the selected items - tblCorpora.asMultiSelect().setValue(oldSelectedItems); - - if (showLoginMessage) { - if (corpora.isEmpty()) { - Notification.show( - "No corpora found. Please login " - + "(use button at upper right corner) to see more corpora.", - Notification.Type.HUMANIZED_MESSAGE); - } - } - }); - } catch (Throwable ex) { - log.warn("Could not get corpus list", ex); - } - - } - } - private static final long serialVersionUID = -6395601812288089382L; - private static final org.slf4j.Logger log = LoggerFactory.getLogger(CorpusListPanel.class); - private static final Resource INFO_ICON = VaadinIcons.INFO_CIRCLE; private static final Resource DOC_ICON = VaadinIcons.FILE_TEXT_O; @@ -128,8 +83,6 @@ public void run() { private final TextField txtFilter; - private final AnnisUI ui; - private final SerializablePredicate filter; private ListDataProvider availableCorpora; @@ -139,9 +92,7 @@ public void run() { private final Column selectedColumn; - public CorpusListPanel(AnnisUI ui) { - this.ui = ui; - + public CorpusListPanel() { setWidthFull(); setHeightFull(); setMargin(true); @@ -195,6 +146,7 @@ public CorpusListPanel(AnnisUI ui) { addComponent(txtFilter); this.filter = corpus -> { + // Always show selected corpora if (tblCorpora.getSelectedItems().contains(corpus)) { return true; @@ -212,10 +164,18 @@ public CorpusListPanel(AnnisUI ui) { CorpusSet selectedCS = null; List corpusSets = new LinkedList<>(); - if (ui.getInstanceConfig() != null && ui.getInstanceConfig().getCorpusSets() != null) { - corpusSets.addAll(ui.getInstanceConfig().getCorpusSets()); + UI ui = getUI(); + if (ui instanceof AnnisUI) { + AnnisUI annisUI = (AnnisUI) ui; + + if (annisUI.getInstanceConfig() != null + && annisUI.getInstanceConfig().getCorpusSets() != null) { + corpusSets.addAll(annisUI.getInstanceConfig().getCorpusSets()); + } } + + for (CorpusSet cs : corpusSets) { if (cs.getName().equals(selectedCorpusSetName)) { selectedCS = cs; @@ -239,12 +199,16 @@ public CorpusListPanel(AnnisUI ui) { final Button l = new Button(); l.setIcon(INFO_ICON); l.setDescription("show metadata and annotations for " + corpus); - l.addClickListener(event -> { - if (ui.getQueryController() != null) { - l.setEnabled(false); - initCorpusBrowser(corpus, l); - } - }); + UI ui = getUI(); + if (ui instanceof AnnisUI) { + AnnisUI annisUI = (AnnisUI) ui; + l.addClickListener(event -> { + if (annisUI.getQueryController() != null) { + l.setEnabled(false); + initCorpusBrowser(corpus, l, annisUI); + } + }); + } l.addStyleNames(ValoTheme.BUTTON_BORDERLESS, ValoTheme.BUTTON_ICON_ONLY, ValoTheme.BUTTON_SMALL); l.setWidthUndefined(); @@ -258,9 +222,13 @@ public CorpusListPanel(AnnisUI ui) { final Button l = new Button(); l.setIcon(DOC_ICON); l.setDescription("opens the document browser for " + corpus); - l.addClickListener(event -> { - ui.getSearchView().getDocBrowserController().openDocBrowser(corpus); - }); + UI ui = getUI(); + if (ui instanceof AnnisUI) { + AnnisUI annisUI = (AnnisUI) ui; + l.addClickListener(event -> { + annisUI.getSearchView().getDocBrowserController().openDocBrowser(corpus); + }); + } l.addStyleNames(ValoTheme.BUTTON_BORDERLESS, ValoTheme.BUTTON_ICON_ONLY, ValoTheme.BUTTON_SMALL); return l; @@ -275,9 +243,16 @@ public CorpusListPanel(AnnisUI ui) { nameColumn.setExpandRatio(10); nameColumn.setStyleGenerator(item -> COMPACT_COLUMN_CLASS); nameColumn.setResizable(false); - - selectedColumn = - tblCorpora.addColumn(corpus -> ui.getQueryState().getSelectedCorpora().contains(corpus)); + + selectedColumn = tblCorpora.addColumn(corpus -> { + UI ui = getUI(); + if (ui instanceof AnnisUI) { + AnnisUI annisUI = (AnnisUI) ui; + return annisUI.getQueryState().getSelectedCorpora().contains(corpus); + } else { + return false; + } + }); selectedColumn.setHidden(true); tblCorpora.setSortOrder( @@ -294,46 +269,51 @@ public CorpusListPanel(AnnisUI ui) { public void attach() { super.attach(); - // Get the initial corpus list, this must become before the binder is set, - // to make sure any selected value is also an item. - CorporaApi api = new CorporaApi(Helper.getClient(ui)); - - try { - List corpora = api.listCorpora(); - availableCorpora = new ListDataProvider<>(corpora); - availableCorpora.setFilter(filter); - tblCorpora.setDataProvider(availableCorpora); - if (ui.getInstanceConfig() != null && ui.getInstanceConfig().getCorpusSets() != null) { - TreeSet corpusSetNames = new TreeSet<>(ui.getInstanceConfig().getCorpusSets() - .stream().map(CorpusSet::getName).collect(Collectors.toList())); - cbSelection.setItems(corpusSetNames); - if (ui.getInstanceConfig().getDefaultCorpusSet() != null - && !ui.getInstanceConfig().getDefaultCorpusSet().isEmpty()) { - cbSelection.setSelectedItem(ui.getInstanceConfig().getDefaultCorpusSet()); + UI ui = getUI(); + if (ui instanceof AnnisUI) { + AnnisUI annisUI = (AnnisUI) ui; + + // Get the initial corpus list, this must become before the binder is set, + // to make sure any selected value is also an item. + try { + List corpora = annisUI.getWebClient().get().uri("/corpora").retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}).block(); + availableCorpora = new ListDataProvider<>(corpora); + availableCorpora.setFilter(filter); + tblCorpora.setDataProvider(availableCorpora); + if (annisUI.getInstanceConfig() != null + && annisUI.getInstanceConfig().getCorpusSets() != null) { + TreeSet corpusSetNames = new TreeSet<>(annisUI.getInstanceConfig().getCorpusSets() + .stream().map(CorpusSet::getName).collect(Collectors.toList())); + cbSelection.setItems(corpusSetNames); + if (annisUI.getInstanceConfig().getDefaultCorpusSet() != null + && !annisUI.getInstanceConfig().getDefaultCorpusSet().isEmpty()) { + cbSelection.setSelectedItem(annisUI.getInstanceConfig().getDefaultCorpusSet()); + } } - } - - if (corpora.isEmpty() && Helper.getUser(ui.getSecurityContext()).isPresent()) { - Notification.show( - "No corpora found. Please login " - + "(use button at upper right corner) to see more corpora.", - Notification.Type.HUMANIZED_MESSAGE); - } + if (corpora.isEmpty() && Helper.getUser(SecurityContextHolder.getContext()).isPresent()) { + Notification.show( + "No corpora found. Please login " + + "(use button at upper right corner) to see more corpora.", + Notification.Type.HUMANIZED_MESSAGE); + } - } catch (ApiException e) { - ExceptionDialog.show(e, "Could not get corpus list", getUI()); - } - Binder binder = ui.getQueryController().getBinder(); - MultiSelect corpusSelection = tblCorpora.asMultiSelect(); - binder.forField(corpusSelection).bind(QueryUIState::getSelectedCorpora, - (state, selectedCorpora) -> ui.getQueryController().setSelectedCorpora(selectedCorpora)); + } catch (WebClientResponseException e) { + ExceptionDialog.show(e, "Could not get corpus list", getUI()); + } + Binder binder = annisUI.getQueryController().getBinder(); + MultiSelect corpusSelection = tblCorpora.asMultiSelect(); + binder.forField(corpusSelection).bind(QueryUIState::getSelectedCorpora, (state, + selectedCorpora) -> annisUI.getQueryController().setSelectedCorpora(selectedCorpora)); - IDGenerator.assignIDForFields(CorpusListPanel.this, tblCorpora, txtFilter); - corpusSelectionChanged(); + + IDGenerator.assignIDForFields(CorpusListPanel.this, tblCorpora, txtFilter); + corpusSelectionChanged(); + } } public void corpusSelectionChanged() { @@ -350,7 +330,7 @@ public Grid getTblCorpora() { return tblCorpora; } - public void initCorpusBrowser(String topLevelCorpusName, final Button l) { + public void initCorpusBrowser(String topLevelCorpusName, final Button l, AnnisUI ui) { MetaDataPanel meta = new MetaDataPanel(topLevelCorpusName); @@ -387,10 +367,33 @@ public void initCorpusBrowser(String topLevelCorpusName, final Button l) { } public void updateCorpusSetList(boolean showLoginMessage) { - if (ui != null) { - ui.clearCorpusConfigCache(); + UI ui = getUI(); + if (ui instanceof AnnisUI) { + AnnisUI annisUI = (AnnisUI) ui; + annisUI.clearCorpusConfigCache(); + + + + annisUI.getWebClient().get().uri("/corpora").retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}).subscribe(corpora -> { + ui.access(() -> { + availableCorpora = new ListDataProvider<>(corpora); + availableCorpora.setFilter(filter); + HashSet oldSelectedItems = new HashSet<>(tblCorpora.getSelectedItems()); + tblCorpora.setDataProvider(availableCorpora); + // reset the selected items + tblCorpora.asMultiSelect().setValue(oldSelectedItems); + + if (showLoginMessage) { + if (corpora.isEmpty()) { + Notification.show( + "No corpora found. Please login " + + "(use button at upper right corner) to see more corpora.", + Notification.Type.HUMANIZED_MESSAGE); + } + } + }); + }); } - CorpusListUpdater updater = new CorpusListUpdater(showLoginMessage); - Background.run(updater); } } diff --git a/src/main/java/org/corpus_tools/annis/gui/controlpanel/QueryPanel.java b/src/main/java/org/corpus_tools/annis/gui/controlpanel/QueryPanel.java index 211dfd98e0..e16bea560d 100644 --- a/src/main/java/org/corpus_tools/annis/gui/controlpanel/QueryPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/controlpanel/QueryPanel.java @@ -42,16 +42,16 @@ import org.corpus_tools.annis.api.model.BadRequestError; import org.corpus_tools.annis.api.model.QueryAttributeDescription; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.ExportPanel; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.HistoryPanel; -import org.corpus_tools.annis.gui.IDGenerator; +import org.corpus_tools.annis.gui.components.ExportPanel; +import org.corpus_tools.annis.gui.components.HistoryPanel; import org.corpus_tools.annis.gui.components.VirtualKeyboardCodeEditor; import org.corpus_tools.annis.gui.components.codemirror.AqlCodeEditor; import org.corpus_tools.annis.gui.frequency.FrequencyQueryPanel; import org.corpus_tools.annis.gui.objects.Query; import org.corpus_tools.annis.gui.objects.QueryUIState; -import org.corpus_tools.annis.gui.querybuilder.QueryBuilderChooser; +import org.corpus_tools.annis.gui.querybuilder.tiger.QueryBuilderChooser; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.annis.gui.util.IDGenerator; import org.vaadin.hene.popupbutton.PopupButton; /** diff --git a/src/main/java/org/corpus_tools/annis/gui/controlpanel/SearchOptionsPanel.java b/src/main/java/org/corpus_tools/annis/gui/controlpanel/SearchOptionsPanel.java index ad57eca6ce..1ec3df1921 100644 --- a/src/main/java/org/corpus_tools/annis/gui/controlpanel/SearchOptionsPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/controlpanel/SearchOptionsPanel.java @@ -25,27 +25,30 @@ import java.util.Collection; import java.util.Comparator; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.api.model.AnnotationComponentType; import org.corpus_tools.annis.api.model.Component; import org.corpus_tools.annis.api.model.CorpusConfiguration; +import org.corpus_tools.annis.api.model.CorpusConfigurationContext; +import org.corpus_tools.annis.api.model.CorpusConfigurationView; import org.corpus_tools.annis.api.model.FindQuery; import org.corpus_tools.annis.api.model.FindQuery.OrderEnum; import org.corpus_tools.annis.api.model.QueryLanguage; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Background; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.objects.CorpusConfigMap; import org.corpus_tools.annis.gui.objects.QueryUIState; +import org.corpus_tools.annis.gui.util.Helper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; /** * @@ -54,70 +57,6 @@ */ public class SearchOptionsPanel extends FormLayout { - private class CorpusConfigUpdater implements Runnable { - - private final AnnisUI ui; - private final Collection corpora; - private final boolean corpusSelectionChanged; - private final boolean setValues; - - public CorpusConfigUpdater(AnnisUI ui, Collection corpora, - boolean corpusSelectionChanged) { - this.ui = ui; - this.corpora = corpora; - this.corpusSelectionChanged = corpusSelectionChanged; - - this.setValues = corpusSelectionChanged && isUpdateStateFromConfig(); - } - - @Override - public void run() { - final List segmentations = getSegmentationNamesFromService(corpora, ui); - - final Set corporaWithDefault = new TreeSet<>(corpora); - corporaWithDefault.add(DEFAULT_CONFIG); - - final CorpusConfigMap corpusConfigs = new CorpusConfigMap(); - for (String c : corporaWithDefault) { - corpusConfigs.put(c, ui.getCorpusConfigWithCache(c)); - } - - // if there are not any defaults create them - if (!corpusConfigs.containsConfig(DEFAULT_CONFIG)) { - corpusConfigs.put(DEFAULT_CONFIG, Helper.getDefaultCorpusConfig()); - } - - // update GUI - ui.access(() -> { - setLoadingState(false); - - CorpusConfiguration c = mergeConfigs(corpora, corpusConfigs); - - if (c.getContext().getMax() == null) { - maxContext.set(Integer.MAX_VALUE); - } else { - maxContext.set(c.getContext().getMax()); - } - - cbLeftContext.setItems(c.getContext().getSizes()); - cbRightContext.setItems(c.getContext().getSizes()); - cbSegmentation.setItems(segmentations); - - if (setValues) { - cbLeftContext.setValue(c.getContext().getDefault()); - cbRightContext.setValue(c.getContext().getDefault()); - cbSegmentation.setValue(c.getContext().getSegmentation()); - cbResultsPerPage.setValue(c.getView().getPageSize()); - } - - // reset if corpus selection has changed - if (corpusSelectionChanged) { - setUpdateStateFromConfig(true); - } - }); - } - - } /** * @@ -125,7 +64,6 @@ public void run() { private static final long serialVersionUID = 7878445496945702778L; private static final String NULL_SEGMENTATION_VALUE = "tokens (default)"; - private static final String DEFAULT_CONFIG = "default-config"; private static final Logger log = LoggerFactory.getLogger(SearchOptionsPanel.class); // TODO: make this configurable @@ -133,29 +71,6 @@ public void run() { public static final List PREDEFINED_CONTEXTS = ImmutableList.of(0, 1, 2, 5, 10, 20); - - private static List getSegmentationNamesFromService(Collection corpora, UI ui) { - List segNames = new ArrayList<>(); - CorporaApi corporaApi = new CorporaApi(Helper.getClient(ui)); - for (String corpus : corpora) { - try { - // Get all ordering components - for(Component c : corporaApi.components(corpus, AnnotationComponentType.ORDERING.getValue(), null)) { - if (!c.getName().isEmpty() && !"annis".equals(c.getLayer())) { - segNames.add(c.getName()); - } - } - } catch (ApiException ex) { - if (ex.getCode() == 403) { - log.debug("Did not have access rights to query segmentation names for corpus", ex); - } else { - log.warn("Could not query segmentation names for corpus", ex); - } - } - } - return segNames; - } - private final com.vaadin.ui.ComboBox cbLeftContext; private final com.vaadin.ui.ComboBox cbRightContext; @@ -299,8 +214,7 @@ public void attach() { cbLeftContext.addSelectionListener(event -> binder.setBean(state)); cbRightContext.addSelectionListener(event -> binder.setBean(state)); - Background - .run(new CorpusConfigUpdater(ui, new LinkedHashSet<>(state.getSelectedCorpora()), false)); + fetchCorpusConfiguration(ui, new LinkedHashSet<>(state.getSelectedCorpora()), false); } } @@ -310,27 +224,46 @@ public boolean isUpdateStateFromConfig() { return updateStateFromConfig; } + private static CorpusConfiguration createEmptyCorpusConfig() { + + final CorpusConfiguration result = new CorpusConfiguration(); + + result.setView(new CorpusConfigurationView()); + result.setContext(new CorpusConfigurationContext()); + result.setExampleQueries(new LinkedList<>()); + result.setVisualizers(new LinkedList<>()); + + result.getView().setPageSize(10); + result.getContext().setDefault(5); + result.getContext().setSizes(Arrays.asList(1, 2, 5, 10)); + result.getContext().setMax(Integer.MAX_VALUE); + + return result; + } + /** * Builds a single config for selection of one or muliple corpora. * * @param corpora Specifies the combination of corpora, for which the config is calculated. - * @param corpusConfigurations A map containg the known corpus configurations. - * @return A new config which takes into account the segementation of all selected corpora. + * @param corpusConfigurations A map containing the known corpus configurations. + * @return A new config which takes into account the segmentation of all selected corpora. */ private CorpusConfiguration mergeConfigs(Collection corpora, - CorpusConfigMap corpusConfigurations) { + Map corpusConfigurations) { List selectedConfigs = corpora.stream().map(c -> corpusConfigurations.get(c)).filter(config -> config != null) .collect(Collectors.toList()); - if (selectedConfigs.size() == 1) { + if (selectedConfigs.isEmpty()) { + return createEmptyCorpusConfig(); + } else if (selectedConfigs.size() == 1) { // Directly return the single corpus configuration return selectedConfigs.get(0); } // Merge the configurations into one - CorpusConfiguration corpusConfig = Helper.getDefaultCorpusConfig(); + CorpusConfiguration corpusConfig = createEmptyCorpusConfig(); // Calculate merged context Optional mergedDefaultCtx = selectedConfigs.stream() @@ -394,6 +327,56 @@ public void setUpdateStateFromConfig(boolean updateStateFromConfig) { this.updateStateFromConfig = updateStateFromConfig; } + private void fetchCorpusConfiguration(AnnisUI ui, Collection corpora, + boolean corpusSelectionChanged) { + final boolean setValues = corpusSelectionChanged && isUpdateStateFromConfig(); + + WebClient client = ui.getWebClient(); + + // Fetch segmentations for all involved corpora + Mono> segmentations = Flux.fromIterable(corpora).flatMap( + corpus -> + client.get() + .uri(ub -> ub.path("/corpora/{corpus}/components") + .queryParam("type", AnnotationComponentType.ORDERING.getValue()).build(corpus)) + .retrieve().bodyToFlux(Component.class) + ).filter( + component -> !component.getName().isEmpty() && !"annis".equals(component.getLayer())) + .map(Component::getName).collectList(); + segmentations.subscribe(result -> ui.access(() -> cbSegmentation.setItems(result))); + + // Fetch the corpus configuration for all involved corpora + Mono> corpusConfigs = + Helper.getCorpusConfigurationMap(corpora, client); + // Update the UI when results are ready + corpusConfigs.subscribe(result -> ui.access(() -> { + setLoadingState(false); + CorpusConfiguration c = mergeConfigs(corpora, result); + if (c.getContext().getMax() == null) { + maxContext.set(Integer.MAX_VALUE); + } else { + maxContext.set(c.getContext().getMax()); + } + + cbLeftContext.setItems(c.getContext().getSizes()); + cbRightContext.setItems(c.getContext().getSizes()); + + + if (setValues) { + cbLeftContext.setValue(c.getContext().getDefault()); + cbRightContext.setValue(c.getContext().getDefault()); + cbSegmentation.setValue(c.getContext().getSegmentation()); + cbResultsPerPage.setValue(c.getView().getPageSize()); + } + + // reset if corpus selection has changed + if (corpusSelectionChanged) { + setUpdateStateFromConfig(true); + } + })); + } + + public void updateSearchPanelConfigurationInBackground(final Collection corpora) { setLoadingState(true); @@ -405,7 +388,7 @@ public void updateSearchPanelConfigurationInBackground(final Collection UI ui = getUI(); if (ui instanceof AnnisUI) { // reload the config in the background - Background.run(new CorpusConfigUpdater((AnnisUI) ui, corpora, true)); + fetchCorpusConfiguration((AnnisUI) ui, corpora, true); } } diff --git a/src/main/java/org/corpus_tools/annis/gui/CaseSensitiveOrder.java b/src/main/java/org/corpus_tools/annis/gui/converter/CaseSensitiveOrder.java similarity index 97% rename from src/main/java/org/corpus_tools/annis/gui/CaseSensitiveOrder.java rename to src/main/java/org/corpus_tools/annis/gui/converter/CaseSensitiveOrder.java index d446d4d72f..2405d0c77f 100644 --- a/src/main/java/org/corpus_tools/annis/gui/CaseSensitiveOrder.java +++ b/src/main/java/org/corpus_tools/annis/gui/converter/CaseSensitiveOrder.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.converter; import com.google.common.collect.ComparisonChain; import java.io.Serializable; diff --git a/src/main/java/org/corpus_tools/annis/gui/converter/CommaSeperatedStringConverterSet.java b/src/main/java/org/corpus_tools/annis/gui/converter/CommaSeperatedStringConverterSet.java index cffaad19b4..c5331b892d 100644 --- a/src/main/java/org/corpus_tools/annis/gui/converter/CommaSeperatedStringConverterSet.java +++ b/src/main/java/org/corpus_tools/annis/gui/converter/CommaSeperatedStringConverterSet.java @@ -19,7 +19,6 @@ import java.util.Collection; import java.util.Locale; import java.util.TreeSet; -import org.corpus_tools.annis.gui.CaseSensitiveOrder; /** * diff --git a/src/main/java/org/corpus_tools/annis/gui/converter/TreeSetConverter.java b/src/main/java/org/corpus_tools/annis/gui/converter/TreeSetConverter.java index 31962b6bcf..3feea83fae 100644 --- a/src/main/java/org/corpus_tools/annis/gui/converter/TreeSetConverter.java +++ b/src/main/java/org/corpus_tools/annis/gui/converter/TreeSetConverter.java @@ -21,7 +21,6 @@ import java.util.Collection; import java.util.Locale; import java.util.TreeSet; -import org.corpus_tools.annis.gui.CaseSensitiveOrder; /** * diff --git a/src/main/java/org/corpus_tools/annis/gui/beans/CorpusBrowserEntry.java b/src/main/java/org/corpus_tools/annis/gui/corpusbrowser/CorpusBrowserEntry.java similarity index 96% rename from src/main/java/org/corpus_tools/annis/gui/beans/CorpusBrowserEntry.java rename to src/main/java/org/corpus_tools/annis/gui/corpusbrowser/CorpusBrowserEntry.java index 35048889bc..1383ed7605 100644 --- a/src/main/java/org/corpus_tools/annis/gui/beans/CorpusBrowserEntry.java +++ b/src/main/java/org/corpus_tools/annis/gui/corpusbrowser/CorpusBrowserEntry.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.beans; +package org.corpus_tools.annis.gui.corpusbrowser; import com.google.common.collect.ComparisonChain; import java.io.Serializable; import java.util.HashSet; import java.util.Set; +import org.corpus_tools.annis.gui.query_references.CitationProvider; /** * diff --git a/src/main/java/org/corpus_tools/annis/gui/corpusbrowser/CorpusBrowserPanel.java b/src/main/java/org/corpus_tools/annis/gui/corpusbrowser/CorpusBrowserPanel.java new file mode 100644 index 0000000000..7e67d8f69f --- /dev/null +++ b/src/main/java/org/corpus_tools/annis/gui/corpusbrowser/CorpusBrowserPanel.java @@ -0,0 +1,369 @@ +/* + * Copyright 2011 Corpuslinguistic working group Humboldt University Berlin. + * + * 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.corpus_tools.annis.gui.corpusbrowser; + +import com.vaadin.event.selection.SelectionEvent; +import com.vaadin.event.selection.SelectionListener; +import com.vaadin.ui.Accordion; +import com.vaadin.ui.Label; +import com.vaadin.ui.Panel; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import org.corpus_tools.annis.api.model.AnnoKey; +import org.corpus_tools.annis.api.model.Annotation; +import org.corpus_tools.annis.api.model.AnnotationComponentType; +import org.corpus_tools.annis.api.model.Component; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.components.ExampleTable; +import org.corpus_tools.annis.gui.controller.QueryController; +import org.corpus_tools.annis.gui.objects.Query; +import org.corpus_tools.annis.gui.objects.QueryLanguage; +import org.corpus_tools.annis.gui.util.Helper; +import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.function.Tuple2; + +/** + * + * @author Thomas Krause {@literal } + */ +public class CorpusBrowserPanel extends Panel { + + private static final org.slf4j.Logger log = LoggerFactory.getLogger(Helper.class); + + private class ExampleListener implements SelectionListener { + + private static final long serialVersionUID = 5456621606184042619L; + + + + @Override + public void selectionChange(SelectionEvent event) { + Optional selected = event.getFirstSelectedItem(); + Set corpusNameSet = new HashSet<>(); + if (corpus != null) { + corpusNameSet.add(corpus); + } + if (controller != null && selected.isPresent()) { + controller.setQuery(new Query(selected.get().getQuery(), QueryLanguage.AQL, corpusNameSet)); + } + } + } + + private static final long serialVersionUID = -1029743017413951838L; + + private String corpus; + + private ExampleTable tblNodeAnno; + + private Label lblNoNodeAnno; + + private ExampleTable tblEdgeTypes; + + private Label lblNoEdgeTypes; + + private ExampleTable tblEdgeAnno; + + private Label lblNoEdgeAnno; + + private ExampleTable tblMetaAnno; + + private Label lblNoMetaAnno; + + private QueryController controller; + + private Accordion accordion; + + public CorpusBrowserPanel() { + this(null, null); + } + + public CorpusBrowserPanel(String corpus, QueryController controller) { + super("Available annotations"); + this.corpus = corpus; + this.controller = controller; + + setSizeFull(); + + tblNodeAnno = new ExampleTable(); + tblEdgeTypes = new ExampleTable(); + tblEdgeAnno = new ExampleTable(); + tblMetaAnno = new ExampleTable(); + + tblNodeAnno.addSelectionListener(new ExampleListener()); + tblEdgeTypes.addSelectionListener(new ExampleListener()); + tblEdgeAnno.addSelectionListener(new ExampleListener()); + tblMetaAnno.addSelectionListener(new ExampleListener()); + + tblNodeAnno.sort("name"); + tblEdgeTypes.sort("name"); + tblEdgeAnno.sort("name"); + + lblNoNodeAnno = new Label("(No Node Annotations)"); + VerticalLayout tabNodeAnno = new VerticalLayout(tblNodeAnno, lblNoNodeAnno); + tabNodeAnno.setCaption("Node Annotations"); + tabNodeAnno.setMargin(false); + + lblNoEdgeAnno = new Label("(No Edge Annotations)"); + VerticalLayout tabEdgeAnno = new VerticalLayout(tblEdgeAnno, lblNoEdgeAnno); + tabEdgeAnno.setCaption("Edge Annotations"); + tabEdgeAnno.setMargin(false); + + lblNoEdgeTypes = new Label("(No Edge Types)"); + VerticalLayout tabEdgeTypes = new VerticalLayout(tblEdgeTypes, lblNoEdgeTypes); + tabEdgeTypes.setCaption("Edge Types"); + tabEdgeTypes.setMargin(false); + + lblNoMetaAnno = new Label("(No Meta Annotations)"); + VerticalLayout tabMetaAnno = new VerticalLayout(tblMetaAnno, lblNoMetaAnno); + tabMetaAnno.setCaption("Meta Annotations"); + tabMetaAnno.setMargin(false); + + accordion = new Accordion(tabNodeAnno, tabEdgeAnno, tabEdgeTypes, tabMetaAnno); + accordion.setSizeFull(); + accordion.setVisible(true); + + setContent(accordion); + + } + + @Override + public void attach() { + super.attach(); + + final UI ui = getUI(); + + if (ui instanceof CommonUI) { + fetchAnnotations((CommonUI) ui); + } + } + + private boolean canExcludeNamespace(Collection annos) { + Set names = new HashSet<>(); + for (Annotation a : annos) { + if (!names.add(a.getKey().getName())) { + return false; + } + } + return true; + } + + private Flux createComponentQuery(WebClient client) { + Flux dominanceComponents = client.get().uri( + ub -> ub.path("/corpora/{corpus}/components").queryParam("type", "Dominance").build(corpus)) + .retrieve().bodyToFlux(Component.class); + + Flux pointingComponents = client.get().uri( + ub -> ub.path("/corpora/{corpus}/components").queryParam("type", "Pointing").build(corpus)) + .retrieve().bodyToFlux(Component.class); + + + return Flux.merge(dominanceComponents, pointingComponents); + } + + private void fetchAnnotations(CommonUI ui) { + + WebClient client = ui.getWebClient(); + + Flux> edgeAnnosByComponent = + createComponentQuery(client).filter(c -> !c.getName().isEmpty()).flatMap(c -> { + Flux componentEdgeAnnos = client.get() + .uri(ub -> ub.path("/corpora/{corpus}/edge-annotations/{type}/{layer}/{name}/") + .queryParam("list_values", true).queryParam("only_most_frequent_values", true) + .build(corpus, c.getType().getValue(), c.getLayer(), c.getName())) + + .retrieve().bodyToFlux(Annotation.class); + return componentEdgeAnnos.zipWith(Mono.just(c).repeat()); + }); + + Flux components = createComponentQuery(client); + + Flux nodeAnnos = client.get() + .uri(ub -> ub.path("/corpora/{corpus}/node-annotations").queryParam("list_values", true) + .queryParam("only_most_frequent_values", true).build(corpus)) + .retrieve().bodyToFlux(Annotation.class) + .filter(a -> !Objects.equals(a.getKey().getNs(), "annis") + && !Objects.equals(a.getKey().getName(), "tok")); + Flux metaAnnoKeys = Helper.getMetaAnnotationNames(corpus, client); + + + showEntries(nodeAnnos.collectList().block(), components.collectList().block(), + edgeAnnosByComponent.collectMultimap(Tuple2::getT2, Tuple2::getT1).block(), + metaAnnoKeys.collectList().block()); + } + + private void showEntries(List nodeAnnos, List components, + Map> edgeAnnosByComponent, List metaAnnoKeysList) { + + Set metaAnnoKeys = new LinkedHashSet<>(metaAnnoKeysList); + + TreeSet nodeAnnoItems = new TreeSet<>(); + TreeSet edgeAnnoItems = new TreeSet<>(); + TreeSet edgeTypeItems = new TreeSet<>(); + TreeSet metaAnnoItems = new TreeSet<>(); + + accordion.setVisible(true); + + + List metaAnnos = new LinkedList<>(nodeAnnos); + nodeAnnos.removeIf(anno -> metaAnnoKeys.contains(anno.getKey())); + metaAnnos.removeIf(anno -> !metaAnnoKeys.contains(anno.getKey())); + + Set allEdgeAnnos = edgeAnnosByComponent.entrySet().stream() + .flatMap(annos -> annos.getValue().stream()).collect(Collectors.toSet()); + + + boolean stripNodeAnno = canExcludeNamespace(nodeAnnos); + boolean stripEdgeName = canExcludeNamespace(allEdgeAnnos); + boolean stripEdgeAnno = true; + HashSet nodeAnnoNames = new HashSet<>(); + HashSet edgeAnnoNames = new HashSet<>(); + HashSet edgeNames = new HashSet<>(); + boolean hasDominance = false; + boolean hasEmptyDominance = false; + + // do some preparations first + for (Annotation a : nodeAnnos) { + // check for ambiguous names + if (!nodeAnnoNames.add(a.getKey().getName())) { + stripNodeAnno = false; + } + } + for (Component c : components) { + // check if collected edge names are unique + if (!edgeNames.add(Helper.getQName(c))) { + stripEdgeName = false; + } + // check if we need to add the general dominance example edge + if (c.getType() == AnnotationComponentType.DOMINANCE) { + hasDominance = true; + if (c.getName() == null || c.getName().isEmpty()) { + hasEmptyDominance = true; + } + } + } + + for (Collection annos : edgeAnnosByComponent.values()) { + for (Annotation a : annos) { + // check for ambiguous names + if (!edgeAnnoNames.add(a.getKey().getName())) { + stripEdgeAnno = false; + } + } + } + + // fill the actual containers + for (Annotation a : nodeAnnos) { + String name = stripNodeAnno ? a.getKey().getName() : Helper.getQName(a.getKey()); + CorpusBrowserEntry cbe = new CorpusBrowserEntry(); + cbe.setName(name); + cbe.setExample(name + "=\"" + a.getVal() + "\""); + cbe.setCorpus(corpus); + nodeAnnoItems.add(cbe); + } + + // edge type entry + if (hasDominance && !hasEmptyDominance) { + CorpusBrowserEntry cbe = new CorpusBrowserEntry(); + cbe.setName("(dominance)"); + cbe.setCorpus(corpus); + cbe.setExample("node & node & #1 > #2"); + edgeTypeItems.add(cbe); + } + for (Component c : components) { + CorpusBrowserEntry cbeEdgeType = new CorpusBrowserEntry(); + String name = stripEdgeName ? c.getName() : Helper.getQName(c); + if ((name == null || name.isEmpty()) && c.getType() == AnnotationComponentType.DOMINANCE) { + cbeEdgeType.setName("(dominance)"); + } else { + cbeEdgeType.setName(name); + } + cbeEdgeType.setCorpus(corpus); + if (c.getType() == AnnotationComponentType.POINTING) { + cbeEdgeType.setExample("node & node & #1 ->" + c.getName() + " #2"); + } else if (c.getType() == AnnotationComponentType.DOMINANCE) { + cbeEdgeType.setExample("node & node & #1 >" + c.getName() + " #2"); + } + edgeTypeItems.add(cbeEdgeType); + } + + // edge annotation entries + for (Map.Entry> entry : edgeAnnosByComponent.entrySet()) { + Component c = entry.getKey(); + for (Annotation a : entry.getValue()) { + CorpusBrowserEntry cbeEdgeAnno = new CorpusBrowserEntry(); + String edgeAnno = stripEdgeAnno ? a.getKey().getName() : Helper.getQName(a.getKey()); + cbeEdgeAnno.setName(edgeAnno); + cbeEdgeAnno.setCorpus(corpus); + if (c.getType() == AnnotationComponentType.POINTING) { + cbeEdgeAnno.setExample("node & node & #1 ->" + c.getName() + "[" + a.getKey().getName() + + "=\"" + a.getVal() + "\"] #2"); + } else if (c.getType() == AnnotationComponentType.DOMINANCE) { + cbeEdgeAnno.setExample( + "node & node & #1 >[" + a.getKey().getName() + "=\"" + a.getVal() + "\"] #2"); + } + edgeAnnoItems.add(cbeEdgeAnno); + } + } + + boolean stripMetaName = canExcludeNamespace(metaAnnos); + for (Annotation a : nodeAnnos) { + String name = stripMetaName ? a.getKey().getName() : Helper.getQName(a.getKey()); + CorpusBrowserEntry cbe = new CorpusBrowserEntry(); + cbe.setName(name); + cbe.setExample(name + "=\"" + a.getVal() + "\""); + cbe.setCorpus(corpus); + nodeAnnoItems.add(cbe); + } + for (Annotation a : metaAnnos) { + String name = stripNodeAnno ? a.getKey().getName() : Helper.getQName(a.getKey()); + CorpusBrowserEntry cbe = new CorpusBrowserEntry(); + cbe.setName(name); + cbe.setExample(name + "=\"" + a.getVal() + "\""); + cbe.setCorpus(corpus); + metaAnnoItems.add(cbe); + } + + lblNoNodeAnno.setVisible(nodeAnnoItems.isEmpty()); + tblNodeAnno.setVisible(!nodeAnnoItems.isEmpty()); + tblNodeAnno.setItems(new ArrayList<>(nodeAnnoItems)); + + lblNoEdgeAnno.setVisible(edgeAnnoItems.isEmpty()); + tblEdgeAnno.setVisible(!edgeAnnoItems.isEmpty()); + tblEdgeAnno.setItems(edgeAnnoItems); + + lblNoEdgeTypes.setVisible(edgeTypeItems.isEmpty()); + tblEdgeTypes.setVisible(!edgeTypeItems.isEmpty()); + tblEdgeTypes.setItems(edgeTypeItems); + + lblNoMetaAnno.setVisible(metaAnnoItems.isEmpty()); + tblMetaAnno.setVisible(!metaAnnoItems.isEmpty()); + tblMetaAnno.setItems(metaAnnoItems); + + } +} diff --git a/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserController.java b/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserController.java index a9f35ca84a..f1740e6573 100644 --- a/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserController.java +++ b/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserController.java @@ -37,17 +37,15 @@ import java.util.Optional; import javax.xml.stream.XMLStreamException; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.api.model.QueryLanguage; import org.corpus_tools.annis.api.model.VisualizerRule; import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.Background; import org.corpus_tools.annis.gui.CommonUI; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.components.ExceptionDialog; import org.corpus_tools.annis.gui.graphml.DocumentGraphMapper; import org.corpus_tools.annis.gui.objects.RawTextWrapper; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.FilteringVisualizerPlugin; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.visualizers.VisualizerPlugin; @@ -59,6 +57,12 @@ import org.eclipse.emf.common.util.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Flux; /** * Represents a global controller for the doc browser feature. @@ -190,8 +194,8 @@ public static VisualizerInput createInput(String corpus, String documentNodeName // get the whole document wrapped in a salt project + WebClient client = ui.getWebClient(); try { - CorporaApi api = new CorporaApi(Helper.getClient(ui)); final SaltProject p = SaltFactory.createSaltProject(); SCorpusGraph cg = p.createCorpusGraph(); @@ -201,20 +205,24 @@ public static VisualizerInput createInput(String corpus, String documentNodeName // Build a query that includes all (possible filtered by name) node of the document String aql = Helper.buildDocumentQuery(documentNodeName, nodeAnnoFilter, useRawText); - File graphML = api.subgraphForQuery(corpus, aql, QueryLanguage.AQL, null); + + File graphML = File.createTempFile("annis-subgraph", ".salt"); + Flux response = client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/subgraph-for-query") + .queryParam("query", aql).queryParam("query_language", QueryLanguage.AQL) + .build(corpus)) + .accept(MediaType.APPLICATION_XML).retrieve().bodyToFlux(DataBuffer.class); + DataBufferUtils.write(response, graphML.toPath()).block(); SDocumentGraph docGraph = DocumentGraphMapper.map(graphML); - if (Files.deleteIfExists(graphML.toPath())) { - log.debug("Could not delete temporary SaltXML file {} because it does not exist.", - graphML.getPath()); - } + Files.deleteIfExists(graphML.toPath()); doc.setDocumentGraph(docGraph); input.setResult(doc); if (useRawText) { input.setRawText(new RawTextWrapper(docGraph)); } - } catch (ApiException e) { + } catch (WebClientResponseException e) { log.error("General remote service exception", e); } catch (XMLStreamException | IOException ex) { log.error("Could not map GraphML to Salt", ex); diff --git a/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserPanel.java b/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserPanel.java index c9428e9076..b6e6917bbb 100644 --- a/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserPanel.java @@ -25,21 +25,18 @@ import com.vaadin.ui.VerticalLayout; import com.vaadin.v7.data.util.filter.SimpleStringFilter; import com.vaadin.v7.ui.themes.ChameleonTheme; +import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.util.Arrays; import java.util.List; import javax.xml.stream.XMLStreamException; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.api.model.AnnotationComponentType; import org.corpus_tools.annis.api.model.QueryLanguage; import org.corpus_tools.annis.api.model.VisualizerRule; import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.Background; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.components.ExceptionDialog; import org.corpus_tools.annis.gui.graphml.CorpusGraphMapper; import org.corpus_tools.annis.gui.objects.DocumentBrowserConfig; @@ -48,6 +45,13 @@ import org.corpus_tools.salt.common.SDocument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Flux; /** * @@ -62,16 +66,20 @@ private class LoadingDocs implements Runnable { @Override public void run() { - CorporaApi api = new CorporaApi(Helper.getClient(ui)); + WebClient client = ui.getWebClient(); try { - File graphML = api.subgraphForQuery(corpus, "annis:node_type=\"corpus\"", - QueryLanguage.AQL, AnnotationComponentType.PARTOF); + File graphML = File.createTempFile("annis-subgraph", ".salt"); + Flux response = client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/subgraph-for-query") + .queryParam("query", "annis:node_type=\"corpus\"") + .queryParam("query_language", QueryLanguage.AQL) + .queryParam("component_type_filter", AnnotationComponentType.PARTOF) + .build(corpus)) + .accept(MediaType.APPLICATION_XML).retrieve().bodyToFlux(DataBuffer.class); + DataBufferUtils.write(response, graphML.toPath()).block(); SCorpusGraph graph = CorpusGraphMapper.map(graphML); - if (Files.deleteIfExists(graphML.toPath())) { - log.debug("Could not delete temporary SaltXML file {} because it does not exist.", - graphML.getPath()); - } + Files.deleteIfExists(graphML.toPath()); List docs = graph.getDocuments(); ui.access(() -> { @@ -95,7 +103,7 @@ public void run() { table.setDocuments(docs); }); - } catch (ApiException | IOException | XMLStreamException ex) { + } catch (WebClientResponseException | IOException | XMLStreamException ex) { ui.access(() -> { ExceptionDialog.show(ex, ui); }); @@ -174,22 +182,23 @@ public DocumentBrowserConfig getDocBrowserConfig() { textVis.setDisplayName("full text"); textVis.setVisType("raw_text"); defaultConfig.setVisualizers(Arrays.asList(new Visualizer(textVis))); - CorporaApi api = new CorporaApi(Helper.getClient(ui)); + WebClient client = ui.getWebClient(); try { - File result = - api.getFile(getCorpus(), - urlPathEscape.escape(getCorpus()) + "/document_browser.json"); - try(FileInputStream is = new FileInputStream(result)) { + byte[] response = client.get() + .uri("/corpora/{corpus}/files/{name}", getCorpus(), + urlPathEscape.escape(getCorpus()) + "/document_browser.json") + .retrieve().bodyToMono(byte[].class).block(); + try (ByteArrayInputStream is = new ByteArrayInputStream(response)) { ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); DocumentBrowserConfig config = mapper.readValue(is, DocumentBrowserConfig.class); return config; } - } catch (ApiException ex) { - if (ex.getCode() != 404) { - ExceptionDialog - .show(ex, "Could not get the document browser configuration file from the backend.", ui); + } catch (WebClientResponseException ex) { + if (ex.getStatusCode() != HttpStatus.NOT_FOUND) { + ExceptionDialog.show(ex, + "Could not get the document browser configuration file from the backend.", ui); } } catch (IOException ex) { diff --git a/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserTable.java b/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserTable.java index e48db4a42c..0efab6bf10 100644 --- a/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserTable.java +++ b/src/main/java/org/corpus_tools/annis/gui/docbrowser/DocBrowserTable.java @@ -41,11 +41,11 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.corpus_tools.annis.api.model.VisualizerRule; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.objects.DocumentBrowserConfig; import org.corpus_tools.annis.gui.objects.MetaDataColumn; import org.corpus_tools.annis.gui.objects.OrderBy; import org.corpus_tools.annis.gui.objects.Visualizer; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.core.SMetaAnnotation; import org.slf4j.Logger; diff --git a/src/main/java/org/corpus_tools/annis/gui/exporter/BaseMatrixExporter.java b/src/main/java/org/corpus_tools/annis/gui/exporter/BaseMatrixExporter.java index e88250d87a..249afd2472 100644 --- a/src/main/java/org/corpus_tools/annis/gui/exporter/BaseMatrixExporter.java +++ b/src/main/java/org/corpus_tools/annis/gui/exporter/BaseMatrixExporter.java @@ -20,6 +20,7 @@ import java.io.Serializable; import java.io.Writer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -32,15 +33,12 @@ import javax.xml.stream.XMLStreamException; import org.apache.commons.io.FileUtils; import org.apache.commons.io.LineIterator; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.api.SearchApi; import org.corpus_tools.annis.api.model.AnnotationComponentType; import org.corpus_tools.annis.api.model.CorpusConfiguration; import org.corpus_tools.annis.api.model.FindQuery; import org.corpus_tools.annis.api.model.QueryAttributeDescription; import org.corpus_tools.annis.api.model.QueryLanguage; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.CommonUI; import org.corpus_tools.salt.common.SCorpusGraph; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.common.SDocumentGraph; @@ -48,6 +46,13 @@ import org.hibernate.cache.CacheException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Flux; /** * An abstract base class for exporters that use Salt subgraphs to some kind of matrix output @@ -71,7 +76,7 @@ public abstract class BaseMatrixExporter implements ExporterPlugin, Serializable * @param args * @param offset */ - private void processFirstPass(SaltProject p, Map args, int offset, int nodeCount) + private void processFirstPass(SaltProject p, Map args, int offset, long nodeCount) throws IOException { int recordNumber = offset; if (p != null && p.getCorpusGraphs() != null) { @@ -87,16 +92,21 @@ private void processFirstPass(SaltProject p, Map args, int offse } private static boolean segmentationNameIsValid(Collection corpora, String segmentation, - UI ui) { - CorporaApi corporaApi = new CorporaApi(Helper.getClient(ui)); + CommonUI ui) { + WebClient client = ui.getWebClient(); for (String corpus : corpora) { try { - if (corporaApi.components(corpus, AnnotationComponentType.ORDERING.getValue(), segmentation) - .isEmpty()) { + Long matchingSegmentations = client.get() + .uri(ub -> ub.path("/corpora/{corpus}/components") + .queryParam("type", AnnotationComponentType.ORDERING.getValue()) + .queryParam("name", segmentation).build(corpus)) + .retrieve().bodyToFlux(org.corpus_tools.annis.api.model.Component.class).count() + .block(); + if (matchingSegmentations == null || matchingSegmentations == 0) { return false; } - } catch (ApiException ex) { - if (ex.getCode() == 403) { + } catch (WebClientResponseException ex) { + if (ex.getStatusCode() == HttpStatus.FORBIDDEN) { log.debug("Did not have access rights to query segmentation names for corpus", ex); } else { log.warn("Could not query segmentation names for corpus", ex); @@ -110,7 +120,7 @@ private static boolean segmentationNameIsValid(Collection corpora, Strin public Exception convertText(String queryAnnisQL, QueryLanguage queryLanguage, int contextLeft, int contextRight, Set corpora, List keys, String argsAsString, boolean alignmc, Writer out, EventBus eventBus, - Map corpusConfigs, UI ui) { + Map corpusConfigs, CommonUI ui) { @@ -125,7 +135,7 @@ public Exception convertText(String queryAnnisQL, QueryLanguage queryLanguage, i args.put(key, val); } - SearchApi searchApi = new SearchApi(Helper.getClient(ui)); + WebClient client = ui.getWebClient(); // Do some validity checks of the arguments, like a segmentation must exist on all selected // corpora @@ -142,25 +152,31 @@ public Exception convertText(String queryAnnisQL, QueryLanguage queryLanguage, i query.setQueryLanguage(queryLanguage); query.setQuery(queryAnnisQL); try { - File matches = searchApi.find(query); + + Flux response = ui.getWebClient().post().uri("/search/find") + .contentType(MediaType.APPLICATION_JSON).bodyValue(query).accept(MediaType.TEXT_PLAIN) + .retrieve().bodyToFlux(DataBuffer.class); + + File matches = File.createTempFile("annis-result", ".txt"); + DataBufferUtils.write(response, matches.toPath()).block(); // Get the node count for the query by parsing it - List nodeDescriptions = - searchApi.nodeDescriptions(queryAnnisQL, queryLanguage); - Integer nodeCount = nodeDescriptions.size(); + Long nodeCount = client.get() + .uri(ub -> ub.path("/search/node-descriptions").queryParam("query", queryAnnisQL) + .queryParam("query_language", queryLanguage).build()) + .retrieve().bodyToFlux(QueryAttributeDescription.class).count().block(); List listOfKeys = new ArrayList<>(); Collections.sort(listOfKeys); // First pass: iterate over all matches and get the sub-graph for them - CorporaApi corporaApi = new CorporaApi(Helper.getClient(ui)); int progress = 0; try (LineIterator lines = FileUtils.lineIterator(matches, StandardCharsets.UTF_8.name())) { int recordNumber = 0; while (lines.hasNext()) { String currentLine = lines.nextLine(); - Optional p = ExportHelper.getSubgraphForMatch(currentLine, corporaApi, + Optional p = ExportHelper.getSubgraphForMatch(currentLine, ui.getWebClient(), contextLeft, contextRight, args, corpusConfigs); if (p.isPresent()) { processFirstPass(p.get(), args, recordNumber++, nodeCount); @@ -187,7 +203,7 @@ public Exception convertText(String queryAnnisQL, QueryLanguage queryLanguage, i try (LineIterator lines = FileUtils.lineIterator(matches, StandardCharsets.UTF_8.name())) { while (lines.hasNext()) { String currentLine = lines.nextLine(); - Optional p = ExportHelper.getSubgraphForMatch(currentLine, corporaApi, + Optional p = ExportHelper.getSubgraphForMatch(currentLine, ui.getWebClient(), contextLeft, contextRight, args, corpusConfigs); if (p.isPresent()) { for (SCorpusGraph cg : p.get().getCorpusGraphs()) { @@ -209,9 +225,11 @@ public Exception convertText(String queryAnnisQL, QueryLanguage queryLanguage, i out.append("\n"); + Files.deleteIfExists(matches.toPath()); + return null; - } catch (ApiException | IOException | CacheException | IllegalStateException + } catch (WebClientResponseException | IOException | CacheException | IllegalStateException | ClassCastException | XMLStreamException ex) { return ex; } @@ -219,7 +237,7 @@ public Exception convertText(String queryAnnisQL, QueryLanguage queryLanguage, i } public abstract void createAdjacencyMatrix(SDocumentGraph graph, Map args, - int recordNumber, int nodeCount) throws IOException; + int recordNumber, long nodeCount) throws IOException; /** * Specifies the ending of export file. diff --git a/src/main/java/org/corpus_tools/annis/gui/exporter/CSVExporter.java b/src/main/java/org/corpus_tools/annis/gui/exporter/CSVExporter.java index 0866fc88ae..f2107a6a23 100644 --- a/src/main/java/org/corpus_tools/annis/gui/exporter/CSVExporter.java +++ b/src/main/java/org/corpus_tools/annis/gui/exporter/CSVExporter.java @@ -35,8 +35,8 @@ import java.util.TreeSet; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.objects.AnnisConstants; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.common.SDocumentGraph; import org.corpus_tools.salt.common.SToken; import org.corpus_tools.salt.core.SAnnotation; @@ -76,7 +76,8 @@ public class CSVExporter extends BaseMatrixExporter { * */ @Override - public void createAdjacencyMatrix(SDocumentGraph graph, Map args, int matchNumber, int nodeCount) + public void createAdjacencyMatrix(SDocumentGraph graph, Map args, + int matchNumber, long nodeCount) throws IOException, IllegalArgumentException { // first match if (matchNumber == 0) { @@ -201,7 +202,8 @@ private void appendMetaLine(SDocumentGraph graph, Writer out, UI ui) throws IOEx String corpusName = Helper.getCorpusPath(graph.getDocument().getId()).get(0); // TODO cache the metadata List metaAnnos = new ArrayList<>(); - for(SNode n : Helper.getMetaData(corpusName, Optional.of(graph.getDocument().getName()), ui).getNodes()) { + for (SNode n : Helper.getMetaData(corpusName, Optional.of(graph.getDocument().getName()), ui) + .block().getNodes()) { metaAnnos.addAll(n.getMetaAnnotations()); } Multimap metaAnnosByName = Multimaps.index(metaAnnos, SMetaAnnotation::getName); diff --git a/src/main/java/org/corpus_tools/annis/gui/exporter/ExportHelper.java b/src/main/java/org/corpus_tools/annis/gui/exporter/ExportHelper.java index 1358f6f795..81442cdf0b 100644 --- a/src/main/java/org/corpus_tools/annis/gui/exporter/ExportHelper.java +++ b/src/main/java/org/corpus_tools/annis/gui/exporter/ExportHelper.java @@ -14,16 +14,14 @@ import java.util.TreeMap; import java.util.TreeSet; import javax.xml.stream.XMLStreamException; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.api.model.AnnotationComponentType; import org.corpus_tools.annis.api.model.Component; import org.corpus_tools.annis.api.model.CorpusConfiguration; import org.corpus_tools.annis.api.model.CorpusConfigurationViewTimelineStrategy.StrategyEnum; import org.corpus_tools.annis.api.model.SubgraphWithContext; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.graphml.DocumentGraphMapper; import org.corpus_tools.annis.gui.objects.Match; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.util.TimelineReconstructor; import org.corpus_tools.salt.SaltFactory; import org.corpus_tools.salt.common.SCorpusGraph; @@ -33,6 +31,12 @@ import org.eclipse.emf.common.util.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Flux; public class ExportHelper { @@ -56,9 +60,9 @@ protected static SaltProject documentGraphToProject(SDocumentGraph graph, return p; } - private static void recreateTimelineIfNecessary(SaltProject p, CorporaApi corporaApi, + private static void recreateTimelineIfNecessary(SaltProject p, WebClient client, Map corpusConfigs) - throws ApiException, UnsupportedEncodingException { + throws WebClientResponseException, UnsupportedEncodingException { Set corpusNames = Helper.getToplevelCorpusNames(p); @@ -82,15 +86,14 @@ private static void recreateTimelineIfNecessary(SaltProject p, CorporaApi corpor } // Get all segmentation names - Set segNames = new TreeSet<>(); - for (Component c : corporaApi.components(firstCorpusName, - AnnotationComponentType.ORDERING.getValue(), null)) { - if (!c.getName().isEmpty() && !"annis".equals(c.getLayer())) { - segNames.add(c.getName()); - } - } - - recreateTimeline(p, timelineStrategy, segNames, config); + List segNames = client.get() + .uri(ub -> ub.path("/corpora/{corpus}/components") + .queryParam("type", AnnotationComponentType.ORDERING.getValue()).build(firstCorpusName)) + .retrieve().bodyToFlux(Component.class) + .filter(c -> !c.getName().isEmpty() && !"annis".equals(c.getLayer())).map(c -> c.getName()) + .collectList().block(); + + recreateTimeline(p, timelineStrategy, new TreeSet<>(segNames), config); } private static void recreateTimeline(SaltProject p, StrategyEnum timelineStrategy, @@ -126,10 +129,10 @@ private static void recreateTimeline(SaltProject p, StrategyEnum timelineStrateg } } - protected static Optional getSubgraphForMatch(String match, CorporaApi corporaApi, + protected static Optional getSubgraphForMatch(String match, WebClient client, int contextLeft, int contextRight, Map args, Map corpusConfigs) - throws ApiException, IOException, XMLStreamException { + throws WebClientResponseException, IOException, XMLStreamException { // iterate over all matches and get the sub-graph for a group of matches Match parsedMatch = Match.parseFromString(match); @@ -149,17 +152,19 @@ protected static Optional getSubgraphForMatch(String match, Corpora subgraphQuery.setSegmentation(args.get(SEGMENTATION_KEY)); } - File graphML = corporaApi.subgraphForNodes(corpusNameForMatch, subgraphQuery); + File graphML = File.createTempFile("annis-subgraph", ".salt"); + Flux response = client.post() + .uri("/corpora/{corpus}/subgraph", corpusNameForMatch) + .accept(MediaType.APPLICATION_XML).bodyValue(subgraphQuery).retrieve() + .bodyToFlux(DataBuffer.class); + DataBufferUtils.write(response, graphML.toPath()).block(); SDocumentGraph docGraph = DocumentGraphMapper.map(graphML); - if (Files.deleteIfExists(graphML.toPath())) { - log.debug("Could not delete temporary SaltXML file {} because it does not exist.", - graphML.getPath()); - } + Files.deleteIfExists(graphML.toPath()); SaltProject p = documentGraphToProject(docGraph, corpusPathForMatch); Helper.addMatchToDocumentGraph(parsedMatch, docGraph); - recreateTimelineIfNecessary(p, corporaApi, corpusConfigs); + recreateTimelineIfNecessary(p, client, corpusConfigs); return Optional.of(p); } diff --git a/src/main/java/org/corpus_tools/annis/gui/exporter/ExporterPlugin.java b/src/main/java/org/corpus_tools/annis/gui/exporter/ExporterPlugin.java index b6f9ef6d40..42aac6def7 100644 --- a/src/main/java/org/corpus_tools/annis/gui/exporter/ExporterPlugin.java +++ b/src/main/java/org/corpus_tools/annis/gui/exporter/ExporterPlugin.java @@ -14,13 +14,13 @@ package org.corpus_tools.annis.gui.exporter; import com.google.common.eventbus.EventBus; -import com.vaadin.ui.UI; import java.io.Writer; import java.util.List; import java.util.Map; import java.util.Set; import org.corpus_tools.annis.api.model.CorpusConfiguration; import org.corpus_tools.annis.api.model.QueryLanguage; +import org.corpus_tools.annis.gui.CommonUI; /** * @@ -30,7 +30,7 @@ public interface ExporterPlugin { public Exception convertText(String query, QueryLanguage queryLanguage, int contextLeft, int contextRight, Set corpora, List keys, String args, boolean alignmc, Writer out, EventBus eventBus, - Map corpusConfigs, UI ui); + Map corpusConfigs, CommonUI ui); public String getFileEnding(); diff --git a/src/main/java/org/corpus_tools/annis/gui/exporter/GeneralTextExporter.java b/src/main/java/org/corpus_tools/annis/gui/exporter/GeneralTextExporter.java index f439c99874..a4780413a7 100644 --- a/src/main/java/org/corpus_tools/annis/gui/exporter/GeneralTextExporter.java +++ b/src/main/java/org/corpus_tools/annis/gui/exporter/GeneralTextExporter.java @@ -24,6 +24,7 @@ import java.io.Serializable; import java.io.Writer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -36,14 +37,12 @@ import javax.xml.stream.XMLStreamException; import org.apache.commons.io.FileUtils; import org.apache.commons.io.LineIterator; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.api.SearchApi; import org.corpus_tools.annis.api.model.Annotation; import org.corpus_tools.annis.api.model.CorpusConfiguration; import org.corpus_tools.annis.api.model.FindQuery; import org.corpus_tools.annis.api.model.QueryLanguage; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.common.SCorpusGraph; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.common.SDocumentGraph; @@ -52,6 +51,12 @@ import org.corpus_tools.salt.core.SFeature; import org.corpus_tools.salt.core.SMetaAnnotation; import org.corpus_tools.salt.core.SNode; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Flux; public abstract class GeneralTextExporter implements ExporterPlugin, Serializable { @@ -70,6 +75,7 @@ public void appendMetaData(Writer out, List metaKeys, String toplevelCor List asList = new ArrayList<>(); for (SNode n : Helper.getMetaData(toplevelCorpus, Optional.ofNullable(documentName), ui) + .block() .getNodes()) { asList.addAll(n.getMetaAnnotations()); } @@ -142,12 +148,11 @@ public void convertText(SaltProject queryResult, List keys, Map corpora, List keys, String argsAsString, boolean alignmc, Writer out, EventBus eventBus, - Map corpusConfigs, UI ui) { + Map corpusConfigs, CommonUI ui) { try { - CorporaApi corporaApi = new CorporaApi(Helper.getClient(ui)); if (keys == null || keys.isEmpty()) { - keys = getAllAnnotationsAsExporterKey(corpora, corporaApi); + keys = getAllAnnotationsAsExporterKey(corpora, ui.getWebClient()); } final List finalKeys = keys; @@ -165,20 +170,24 @@ public Exception convertText(String queryAnnisQL, QueryLanguage queryLanguage, i // 1. Get all the matches as Salt ID - SearchApi searchApi = new SearchApi(Helper.getClient(ui)); - FindQuery query = new FindQuery(); query.setCorpora(new LinkedList(corpora)); query.setQueryLanguage(queryLanguage); query.setQuery(queryAnnisQL); final AtomicInteger offset = new AtomicInteger(); - File matches = searchApi.find(query); + Flux response = + ui.getWebClient().post().uri("/search/find").contentType(MediaType.APPLICATION_JSON) + .bodyValue(query).accept(MediaType.TEXT_PLAIN).retrieve() + .bodyToFlux(DataBuffer.class); + + File matches = File.createTempFile("annis-result", ".txt"); + DataBufferUtils.write(response, matches.toPath()).block(); // 2. iterate over all matches and get the sub-graph for them try (LineIterator lines = FileUtils.lineIterator(matches, StandardCharsets.UTF_8.name())) { while (lines.hasNext()) { String currentLine = lines.nextLine(); - Optional p = ExportHelper.getSubgraphForMatch(currentLine, corporaApi, + Optional p = ExportHelper.getSubgraphForMatch(currentLine, ui.getWebClient(), contextLeft, contextRight, args, corpusConfigs); if (p.isPresent()) { int currentOffset = offset.getAndIncrement(); @@ -194,10 +203,11 @@ public Exception convertText(String queryAnnisQL, QueryLanguage queryLanguage, i } } } + Files.deleteIfExists(matches.toPath()); return null; - } catch (ApiException | IOException | XMLStreamException ex) { + } catch (WebClientResponseException | IOException | XMLStreamException ex) { return ex; } } @@ -223,16 +233,23 @@ public boolean needsContext() { * @param corpora The corpora to query * @param api An API object used to perform the lookup * @return A list of annotation names. - * @throws ApiException + * @throws WebClientResponseException */ protected List getAllAnnotationsAsExporterKey(Collection corpora, - CorporaApi api) throws ApiException { + WebClient client) throws WebClientResponseException { LinkedList keys = new LinkedList<>(); keys.add("tok"); List attributes = new LinkedList<>(); for (String corpus : corpora) { - attributes.addAll(api.nodeAnnotations(corpus, false, false)); + Iterable allAnnotations = client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/node-annotations") + .queryParam("list_values", false).queryParam("only_most_frequent_values", false) + .build(corpus)) + .retrieve().bodyToFlux(Annotation.class).toIterable(); + for (Annotation a : allAnnotations) { + attributes.add(a); + } } for (Annotation a : attributes) { diff --git a/src/main/java/org/corpus_tools/annis/gui/exporter/GridExporter.java b/src/main/java/org/corpus_tools/annis/gui/exporter/GridExporter.java index 60ec46a450..809621cb41 100644 --- a/src/main/java/org/corpus_tools/annis/gui/exporter/GridExporter.java +++ b/src/main/java/org/corpus_tools/annis/gui/exporter/GridExporter.java @@ -24,7 +24,7 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.common.SCorpusGraph; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.common.SDocumentGraph; diff --git a/src/main/java/org/corpus_tools/annis/gui/exporter/TextColumnExporter.java b/src/main/java/org/corpus_tools/annis/gui/exporter/TextColumnExporter.java index 939e9ccea4..f41517248b 100644 --- a/src/main/java/org/corpus_tools/annis/gui/exporter/TextColumnExporter.java +++ b/src/main/java/org/corpus_tools/annis/gui/exporter/TextColumnExporter.java @@ -34,8 +34,8 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.objects.AnnisConstants; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.common.SDocumentGraph; import org.corpus_tools.salt.common.SDominanceRelation; import org.corpus_tools.salt.common.SSpanningRelation; @@ -280,7 +280,8 @@ private List calculateOrderedMatchNumbersGlobally(int[][] adjacencyMatrix, * returned for the user query */ @Override - public void createAdjacencyMatrix(SDocumentGraph graph, Map args, int recordNumber, int nodeCount) + public void createAdjacencyMatrix(SDocumentGraph graph, Map args, + int recordNumber, long nodeCount) throws IOException { String currSpeakerName = ""; String prevSpeakerName = ""; @@ -294,7 +295,7 @@ public void createAdjacencyMatrix(SDocumentGraph graph, Map args filterNumbersSetByUser.clear(); filterNumbersIsEmpty = true; listOfMetakeys.clear(); - adjacencyMatrix = new int[nodeCount][nodeCount]; + adjacencyMatrix = new int[(int) nodeCount][(int) nodeCount]; matrixIsFilled = false; singleMatchesGlobal.clear(); orderedMatchNumbersGlobal.clear(); @@ -637,7 +638,8 @@ private void writeSpeakerHeader(SDocumentGraph graph, int recordNumber, Writer o corpusName = urlPathEscape.escape(corpusName); List metadata = new ArrayList<>(); - for(SNode n : Helper.getMetaData(corpusName, Optional.of(docName), ui).getNodes()) { + for (SNode n : Helper.getMetaData(corpusName, Optional.of(docName), ui).block() + .getNodes()) { metadata.addAll(n.getMetaAnnotations()); } diff --git a/src/main/java/org/corpus_tools/annis/gui/exporter/TokenExporter.java b/src/main/java/org/corpus_tools/annis/gui/exporter/TokenExporter.java index 93c9624121..79675240d9 100644 --- a/src/main/java/org/corpus_tools/annis/gui/exporter/TokenExporter.java +++ b/src/main/java/org/corpus_tools/annis/gui/exporter/TokenExporter.java @@ -26,7 +26,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.common.SCorpusGraph; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.common.SDocumentGraph; diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/FlatQueryBuilder.java b/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/FlatQueryBuilder.java deleted file mode 100644 index 7ba034fc33..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/FlatQueryBuilder.java +++ /dev/null @@ -1,648 +0,0 @@ - -/* - * Copyright 2013 Corpuslinguistic working group Humboldt University Berlin. - * - * 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.corpus_tools.annis.gui.flatquerybuilder; - -import com.vaadin.data.Binder; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.Button; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.MenuBar; -import com.vaadin.ui.MenuBar.MenuItem; -import com.vaadin.ui.Notification; -import com.vaadin.ui.Panel; -import com.vaadin.ui.UI; -import com.vaadin.ui.VerticalLayout; -import com.vaadin.v7.ui.NativeSelect; -import com.vaadin.v7.ui.themes.ChameleonTheme; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.api.model.AnnoKey; -import org.corpus_tools.annis.api.model.Annotation; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.IDGenerator; -import org.corpus_tools.annis.gui.QueryController; -import org.corpus_tools.annis.gui.objects.Query; -import org.corpus_tools.annis.gui.objects.QueryLanguage; -import org.corpus_tools.annis.gui.objects.QueryUIState; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/* - * @author martin klotz (martin.klotz@hu-berlin.de) - * - * @author tom ruette (tom.ruette@hu-berlin.de) - */ -public class FlatQueryBuilder extends Panel implements Button.ClickListener { - - /** - * - */ - private static final long serialVersionUID = -1659782316940380300L; - private static final Logger log = LoggerFactory.getLogger(FlatQueryBuilder.class); - - private static final String[] REGEX_CHARACTERS = - {"\\", "+", ".", "[", "*", "^", "$", "|", "?", "(", ")"}; - private static final String BUTTON_GO_LABEL = "Create AQL Query"; - private static final String BUTTON_CLEAR_LABEL = "Clear the Query Builder"; - private static final String NO_CORPORA_WARNING = - "No corpora selected, please select " + "at least one corpus."; - private static final String INCOMPLETE_QUERY_WARNING = "Query seems to be incomplete."; - private static final String ADD_LING_PARAM = "Add"; - private static final String ADD_SPAN_PARAM = "Add"; - private static final String CHANGE_SPAN_PARAM = "Change"; - private static final String ADD_META_PARAM = "Add"; - private static final String INFO_INIT_LANG = "In this part of the Query Builder, " - + "blocks of the linguistic query can be constructed from left to right."; - private static final String INFO_INIT_SPAN = "This part of the Query Builder " - + "allows you to define a span annotation within which the above query blocks " - + "are confined."; - private static final String INFO_INIT_META = - "Here, you can constrain the linguistic " + "query by selecting meta levels."; - private static final String INFO_FILTER = "When searching in the fields, the " - + "hits are sorted and filtered according to different mechanisms. Please " - + "choose a filtering mechanism here."; - private static final String TOOLBAR_CAPTION = "Toolbar"; - private static final String META_CAPTION = "Meta information"; - - private static final String SPAN_CAPTION = "Scope"; - - private static final String LANG_CAPTION = "Linguistic sequence"; - private static final String ADVANCED_CAPTION = "Advanced settings"; - private Button btGo; - private Button btClear; - - private Button btInitSpan; - private Button btInitMeta; - private Button btInitLanguage; - private MenuBar addMenu; - - private MenuBar addMenuSpan; - private MenuBar addMenuMeta; - private QueryController cp; - private HorizontalLayout language; - - private HorizontalLayout languagenodes; - private HorizontalLayout span; - private HorizontalLayout meta; - private HorizontalLayout toolbar; - private HorizontalLayout advanced; - - private VerticalLayout mainLayout; - - private NativeSelect filtering; - - private Collection vnodes; - - private Collection eboxes; - - private Collection mboxes; - - private SpanBox spbox; - - private MenuBar.MenuItem spanMenu; - - private ReducingStringComparator rsc; - - public FlatQueryBuilder(QueryController cp) { - setSizeFull(); - launch(cp); - - } - - public void addLinguisticSequenceBox(String annoName) { - if (!vnodes.isEmpty()) { - EdgeBox eb = new EdgeBox(this); - languagenodes.addComponent(eb); - eboxes.add(eb); - } - VerticalNode vn = new VerticalNode(annoName, this); - languagenodes.addComponent(vn); - vnodes.add(vn); - addMenu.setAutoOpen(false); - } - - - void addSpanBox(String level) { - spbox = new SpanBox(level, this); - span.addComponent(spbox); - span.setComponentAlignment(spbox, Alignment.MIDDLE_LEFT); - spanMenu.setText(CHANGE_SPAN_PARAM); - } - - void addMetaBox(String annoname) { - MetaBox mb = new MetaBox(annoname, this); - meta.addComponent(mb); - mboxes.add(mb); - addMenuMeta.setAutoOpen(false); - for(MenuItem textItem : addMenuMeta.getItems()) { - if ("Add".equals(textItem.getText())) { - for (MenuItem item : textItem.getChildren()) { - if (annoname.equals(item.getText())) { - item.setVisible(false); - } - } - } - } - } - - @Override - public void attach() { - super.attach(); - - Binder binder = new Binder<>(); - binder.addValueChangeListener(e -> this.initialize()); - binder.setBean(cp.getState()); - - IDGenerator.assignIDForFields(this, language, btInitLanguage, btGo); - } - - @Override - public void buttonClick(Button.ClickEvent event) { - if (cp.getState().getSelectedCorpora().isEmpty()) { - Notification.show(NO_CORPORA_WARNING); - } else { - if (event.getButton() == btGo) { - updateQuery(); - } - - if (event.getButton() == btClear) { - clear(); - updateQuery(); - launch(cp); - } - - if (event.getComponent() == btInitMeta || event.getComponent() == btInitSpan - || event.getComponent() == btInitLanguage) { - initialize(); - } - } - } - - private void clear() - // check whether it is necessary to do this in a method - { - language.removeAllComponents(); - span.removeAllComponents(); - meta.removeAllComponents(); - toolbar.removeAllComponents(); - mainLayout.removeComponent(language); - mainLayout.removeComponent(span); - mainLayout.removeComponent(meta); - mainLayout.removeComponent(toolbar); - vnodes.clear(); - eboxes.clear(); - mboxes.clear(); - } - - public String escapeRegexCharacters(String tok) { - if (tok == null) { - return ""; - } - if (tok.equals("")) { - return ""; - } - String result = tok; - for (int i = 0; i < REGEX_CHARACTERS.length; i++) { - result = result.replace(REGEX_CHARACTERS[i], "\\" + REGEX_CHARACTERS[i]); - } - - return result.replace("/", "\\x2F"); - } - - public Collection getAnnotationValues(String level) { - Collection values = new TreeSet<>(); - for (String s : getAvailableAnnotationLevels(level)) { - values.add(s); - } - return values; - } - - private String getAQLFragment(SearchBox sb) { - String result = ""; - String value = null; - try { - value = sb.getValue(); - } catch (java.lang.NullPointerException ex) { - value = null; - } - String level = sb.getAttribute(); - if (value == null) { - result = level; - } - if (value != null) { - if (sb.isRegEx() && !sb.isNegativeSearch()) { - result = level + "=/" + value.replace("/", "\\x2F") + "/"; - } - if (sb.isRegEx() && sb.isNegativeSearch()) { - result = level + "!=/" + value.replace("/", "\\x2F") + "/"; - } - if (!sb.isRegEx() && sb.isNegativeSearch()) { - result = level + "!=\"" + value.replace("\"", "\\x22") + "\""; - } - if (!sb.isRegEx() && !sb.isNegativeSearch()) { - result = level + "=\"" + value.replace("\"", "\\x22") + "\""; - } - } - return result; - } - - private String getAQLQuery() { - int count = 1; - StringBuilder ql = new StringBuilder(); - StringBuilder edgeQuery = new StringBuilder(); - StringBuilder sentenceQuery = new StringBuilder(); - Collection sentenceVars = new ArrayList<>(); - Iterator itEboxes = eboxes.iterator(); - for (VerticalNode v : vnodes) { - Collection sboxes = v.getSearchBoxes(); - for (SearchBox s : sboxes) { - ql.append(" & " + getAQLFragment(s)); - } - if (sboxes.isEmpty()) { - // not sure we want to do it this way: - ql.append("\n& /.*/"); - } - sentenceVars.add(Integer.valueOf(count)); - for (int i = 1; i < sboxes.size(); i++) { - String addQuery = "\n& #" + count + "_=_" + "#" + ++count; - edgeQuery.append(addQuery); - } - count++; - String edgeQueryAdds = (itEboxes.hasNext()) - ? "\n& #" + (count - 1) + " " + itEboxes.next().getValue() + " #" + count - : ""; - edgeQuery.append(edgeQueryAdds); - } - String addQuery = ""; - try { - SpanBox spb = (SpanBox) span.getComponent(1); - if ((!spb.isRegEx()) && (!spb.getValue().isEmpty())) { - addQuery = "\n& " + spb.getAttribute() + " = \"" + spb.getValue() + "\""; - } - if (spb.isRegEx()) { - addQuery = "\n& " + spb.getAttribute() + " = /" - + spb.getValue().replace("/", "\\x2F") + "/"; - } - if (spb.getValue().isEmpty()) { - addQuery = "\n&" + spb.getAttribute(); - } - ql.append(addQuery); - for (Integer i : sentenceVars) { - sentenceQuery.append("\n& #").append(count).append("_i_#").append(i.toString()); - } - } catch (Exception ex) { - // Ignore this exception - } - StringBuilder metaQuery = new StringBuilder(); - Iterator itMetaBoxes = mboxes.iterator(); - while (itMetaBoxes.hasNext()) { - metaQuery.append(getMetaQueryFragment(itMetaBoxes.next())); - } - String fullQuery = (ql.toString() + edgeQuery.toString() + sentenceQuery.toString() - + metaQuery.toString()); - if (fullQuery.length() < 3) { - return ""; - } - fullQuery = fullQuery.substring(3);// deletes leading " & " - return fullQuery; - } - - public Collection getAvailableAnnotationLevels(String meta) { - Collection result = new TreeSet<>(); - CorporaApi api = new CorporaApi(Helper.getClient(UI.getCurrent())); - // get current corpus selection - Collection corpusSelection = cp.getState().getSelectedCorpora(); - try { - List atts = new LinkedList<>(); - for (String corpus : corpusSelection) { - atts.addAll(api.nodeAnnotations(corpus, true, false)); - } - for (Annotation a : atts) { - if (a.getKey().getName().equals(meta)) { - result.add(a.getVal()); - } - } - } catch (ApiException ex) { - log.error(null, ex); - } - - return result; - } - - public Set getAvailableAnnotationNames() { - Set result = new TreeSet<>(); - // get current corpus selection - Collection corpusSelection = cp.getState().getSelectedCorpora(); - CorporaApi api = new CorporaApi(Helper.getClient(UI.getCurrent())); - try { - for (String corpus : corpusSelection) { - for (Annotation a : api.nodeAnnotations(corpus, false, false)) { - result.add(a.getKey().getName()); - } - } - } catch (ApiException ex) { - log.error(null, ex); - } - result.add("tok"); - return result; - } - - public Set getAvailableMetaNames() { - Set result = new TreeSet<>(); - // get current corpus selection - Collection corpusSelection = cp.getState().getSelectedCorpora(); - try { - for (String corpus : corpusSelection) { - for (AnnoKey key : Helper.getMetaAnnotationNames(corpus, UI.getCurrent())) { - result.add(key.getName()); - } - } - - } catch (ApiException ex) { - log.error(null, ex); - } - - return result; - } - - public String getFilterMechanism() { - return filtering.getItemCaption(filtering.getValue()); - } - - private String getMetaQueryFragment(MetaBox mb) { - Collection values = mb.getValues(); - if (!values.isEmpty()) { - StringBuilder result = new StringBuilder("\n& meta::" + mb.getMetaDatum() + " = "); - if (values.size() == 1) { - result.append("\"" + values.iterator().next().replace("\"", "\\x22") + "\""); - } else { - Iterator itValues = values.iterator(); - result.append("/(" + escapeRegexCharacters(itValues.next()) + ")"); - while (itValues.hasNext()) { - result.append("|(" + escapeRegexCharacters(itValues.next()) + ")"); - } - result.append("/"); - } - return result.toString(); - } - return ""; - } - - public ReducingStringComparator getRSC() { - return rsc; - } - - private void initialize() { - // try to remove all existing menus - try { - if (addMenu != null) { - language.removeComponent(addMenu); - } - if (addMenuSpan != null) { - span.removeComponent(addMenuSpan); - } - if (addMenuMeta != null) { - meta.removeComponent(addMenuMeta); - } - } catch (Exception e) { - log.error(null, e); - } - - // init variables: - final FlatQueryBuilder sq = this; - Collection annonames = getAvailableAnnotationNames(); - Collection metanames = getAvailableMetaNames(); - - // Code from btInitLanguage: - addMenu = new MenuBar(); - // addMenu.setDescription(INFO_INIT_LANG); - addMenu.setAutoOpen(false); - final MenuBar.MenuItem add = addMenu.addItem(ADD_LING_PARAM, null); - for (final String annoname : annonames) { - add.addItem(annoname, selectedItem -> addLinguisticSequenceBox(annoname)); - } - language.removeComponent(btInitLanguage); - language.addComponent(addMenu); - - // Code from btInitSpan: - addMenuSpan = new MenuBar(); - // addMenuSpan.setDescription(INFO_INIT_SPAN); - addMenuSpan.setAutoOpen(false); - final MenuBar.MenuItem addSpan = addMenuSpan.addItem(ADD_SPAN_PARAM, null); - for (final String annoname : annonames) { - addSpan.addItem(annoname, selectedItem -> { - sq.removeSpanBox(); - sq.addSpanBox(annoname); - addMenuSpan.setAutoOpen(false); - }); - } - spanMenu = addSpan; - span.removeComponent(btInitSpan); - span.addComponent(addMenuSpan); - - // Code from btInitMeta: - addMenuMeta = new MenuBar(); - // addMenuMeta.setDescription(INFO_INIT_META); - addMenuMeta.setAutoOpen(false); - final MenuBar.MenuItem addMeta = addMenuMeta.addItem(ADD_META_PARAM, null); - for (final String annoname : metanames) { - addMeta.addItem(annoname, selectedItem -> addMetaBox(annoname)); - } - meta.removeComponent(btInitMeta); - meta.addComponent(addMenuMeta); - } - - private void launch(QueryController cp) { - this.cp = cp; - rsc = new ReducingStringComparator(); - mainLayout = new VerticalLayout(); - // tracking lists for vertical nodes, edgeboxes and metaboxes - vnodes = new ArrayList<>(); - eboxes = new ArrayList<>(); - mboxes = new ArrayList<>(); - spbox = null; - // buttons and checks - btGo = new Button(BUTTON_GO_LABEL, this); - btGo.setStyleName(ChameleonTheme.BUTTON_SMALL); - btClear = new Button(BUTTON_CLEAR_LABEL, this); - btClear.setStyleName(ChameleonTheme.BUTTON_SMALL); - btInitLanguage = new Button("Initialize", this); - btInitLanguage.setDescription(INFO_INIT_LANG); - btInitSpan = new Button("Initialize", this); - btInitSpan.setDescription(INFO_INIT_SPAN); - btInitMeta = new Button("Initialize", this); - btInitMeta.setDescription(INFO_INIT_META); - filtering = new NativeSelect("Filtering mechanisms"); - filtering.setDescription(INFO_FILTER); - ReducingStringComparator rdc = new ReducingStringComparator(); - Set mappings = rdc.getMappings().keySet(); - int i; - for (i = 0; i < mappings.size(); i++) { - String mapname = (String) mappings.toArray()[i]; - filtering.addItem(i); - filtering.setItemCaption(i, mapname); - } - filtering.addItem(i + 1); - filtering.setItemCaption(i + 1, "generic"); - filtering.select(i + 1); - filtering.setNullSelectionAllowed(false); - filtering.setImmediate(true); - // language layout - language = new HorizontalLayout(); - languagenodes = new HorizontalLayout(); - language.addComponent(languagenodes); - language.addComponent(btInitLanguage); - language.setMargin(true); - language.setCaption(LANG_CAPTION); - language.addStyleName("linguistics-panel"); - // span layout - span = new HorizontalLayout(); - span.setSpacing(true); - span.addComponent(btInitSpan); - span.setMargin(true); - span.setCaption(SPAN_CAPTION); - span.addStyleName("span-panel"); - // meta layout - meta = new HorizontalLayout(); - meta.setSpacing(true); - meta.addComponent(btInitMeta); - meta.setMargin(true); - meta.setCaption(META_CAPTION); - meta.addStyleName("meta-panel"); - // toolbar layout - toolbar = new HorizontalLayout(); - toolbar.setSpacing(true); - toolbar.addComponent(btGo); - toolbar.addComponent(btClear); - toolbar.setMargin(true); - toolbar.setCaption(TOOLBAR_CAPTION); - toolbar.addStyleName("toolbar-panel"); - // advanced - advanced = new HorizontalLayout(); - advanced.setSpacing(true); - advanced.addComponent(filtering); - advanced.setMargin(true); - advanced.setCaption(ADVANCED_CAPTION); - advanced.addStyleName("advanced-panel"); - // put everything on the layout - mainLayout.setSpacing(true); - mainLayout.addComponent(language); - mainLayout.addComponent(span); - mainLayout.addComponent(meta); - mainLayout.addComponent(toolbar); - mainLayout.addComponent(advanced); - setContent(mainLayout); - getContent().setWidth("100%"); - getContent().setHeight("-1px"); - } - - public void removeMetaBox(MetaBox v) { - meta.removeComponent(v); - mboxes.remove(v); - List items = addMenuMeta.getItems().get(0).getChildren(); - boolean found = false; - String metalevel = v.getMetaDatum(); - for (int i = 0; (i < items.size()) && !found; i++) { - MenuBar.MenuItem itm = items.get(i); - if (itm.getText().equals(metalevel)) { - itm.setVisible(true); - found = true; - } - } - } - - public void removeSpanBox() { - if (spbox != null) { - span.removeComponent(spbox); - spbox = null; - spanMenu.setText(ADD_SPAN_PARAM); - } - } - - public void removeSpanBox(SpanBox spb) { - if (spb.equals(spbox)) { - removeSpanBox(); - } - } - - public void removeVerticalNode(VerticalNode v) { - Iterator itVnodes = vnodes.iterator(); - Iterator itEboxes = eboxes.iterator(); - VerticalNode vn = itVnodes.next(); - EdgeBox eb = null; - - while (!vn.equals(v)) { - vn = itVnodes.next(); - eb = itEboxes.next(); - } - - if ((eb == null) && (itEboxes.hasNext())) { - eb = itEboxes.next(); - } - - vnodes.remove(v); - if (eb != null) { - eboxes.remove(eb); - languagenodes.removeComponent(eb); - } - languagenodes.removeComponent(v); - } - - - public String unescape(String s) { - // first unescape slashes and quotes: - - s = unescapeSlQ(s); - - // unescape regex characters: - int i = 1; - while (i < s.length()) { - char c0 = s.charAt(i - 1); - char c1 = s.charAt(i); - for (int j = 0; j < REGEX_CHARACTERS.length; j++) { - if ((c1 == REGEX_CHARACTERS[j].charAt(0)) && (c0 == '\\')) { - s = s.substring(0, i - 1) + s.substring(i); - break; - } - if (j == REGEX_CHARACTERS.length - 1) { - i++; - } - } - } - - return s; - } - - public String unescapeSlQ(String s) { - return s.replace("\\x2F", "/").replace("\\x22", "\""); - } - - public void updateQuery() { - try { - cp.setQuery(new Query(getAQLQuery(), QueryLanguage.AQL, - new LinkedHashSet<>(cp.getState().getSelectedCorpora()))); - } catch (java.lang.NullPointerException ex) { - Notification.show(INCOMPLETE_QUERY_WARNING); - } - } -} diff --git a/src/main/java/org/corpus_tools/annis/gui/frequency/FrequencyQueryPanel.java b/src/main/java/org/corpus_tools/annis/gui/frequency/FrequencyQueryPanel.java index 3a4951461a..e4a737e4e6 100644 --- a/src/main/java/org/corpus_tools/annis/gui/frequency/FrequencyQueryPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/frequency/FrequencyQueryPanel.java @@ -43,17 +43,18 @@ import java.util.List; import java.util.Set; import java.util.WeakHashMap; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.SearchApi; import org.corpus_tools.annis.api.model.FrequencyTableRow; import org.corpus_tools.annis.api.model.QueryAttributeDescription; import org.corpus_tools.annis.api.model.QueryLanguage; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.QueryController; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.controller.QueryController; import org.corpus_tools.annis.gui.objects.FrequencyQuery; import org.corpus_tools.annis.gui.objects.QueryUIState; +import org.corpus_tools.annis.gui.util.Helper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * @@ -239,7 +240,7 @@ public FrequencyQueryPanel(final QueryController controller, QueryUIState state) try { nodes = parseQuery(FrequencyQueryPanel.this.state.getAql().getValue(), FrequencyQueryPanel.this.state.getQueryLanguage()); - } catch (ApiException e) { + } catch (WebClientResponseException e) { // Ignore } nr = Math.min(nr, nodes.size() - 1); @@ -447,7 +448,7 @@ private void createAutomaticEntriesForQuery(String query, } } } - } catch (ApiException ex) { + } catch (WebClientResponseException ex) { // non-valid query, ignore } @@ -466,14 +467,22 @@ public void notifiyQueryFinished() { } private List parseQuery(String query, QueryLanguage queryLanguage) - throws ApiException { - if (query == null || query.isEmpty()) { + throws WebClientResponseException { + + UI ui = UI.getCurrent(); + if (query == null || query.isEmpty() || !(ui instanceof CommonUI)) { return new LinkedList<>(); } // let the service parse the query - SearchApi api = new SearchApi(Helper.getClient(UI.getCurrent())); - List nodes = api.nodeDescriptions(query, queryLanguage); + WebClient client = ((CommonUI) ui).getWebClient(); + List nodes = + client.get() + .uri(ub -> ub.path("/search/node-descriptions").queryParam("query", query) + .queryParam("query_language", queryLanguage).build()) + .retrieve().bodyToFlux(QueryAttributeDescription.class).collectList().block(); return nodes; + + } diff --git a/src/main/java/org/corpus_tools/annis/gui/frequency/FrequencyResultPanel.java b/src/main/java/org/corpus_tools/annis/gui/frequency/FrequencyResultPanel.java index c6a06981f9..c9b7addfb3 100644 --- a/src/main/java/org/corpus_tools/annis/gui/frequency/FrequencyResultPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/frequency/FrequencyResultPanel.java @@ -47,10 +47,10 @@ import java.util.List; import java.util.Locale; import org.corpus_tools.annis.api.model.FrequencyTableRow; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.components.FrequencyChart; import org.corpus_tools.annis.gui.objects.FrequencyQuery; import org.corpus_tools.annis.gui.objects.FrequencyTableEntry; +import org.corpus_tools.annis.gui.util.Helper; import org.slf4j.LoggerFactory; /** diff --git a/src/main/java/org/corpus_tools/annis/gui/graphml/AbstractGraphMLMapper.java b/src/main/java/org/corpus_tools/annis/gui/graphml/AbstractGraphMLMapper.java index 5581fd73f7..86a0a61aad 100644 --- a/src/main/java/org/corpus_tools/annis/gui/graphml/AbstractGraphMLMapper.java +++ b/src/main/java/org/corpus_tools/annis/gui/graphml/AbstractGraphMLMapper.java @@ -15,7 +15,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.corpus_tools.annis.api.model.AnnotationComponentType; import org.corpus_tools.annis.api.model.Component; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.SaltFactory; import org.corpus_tools.salt.core.SAnnotationContainer; import org.corpus_tools.salt.core.SFeature; diff --git a/src/main/java/org/corpus_tools/annis/gui/graphml/DocumentGraphMapper.java b/src/main/java/org/corpus_tools/annis/gui/graphml/DocumentGraphMapper.java index 97f85d64bd..8369a584ef 100644 --- a/src/main/java/org/corpus_tools/annis/gui/graphml/DocumentGraphMapper.java +++ b/src/main/java/org/corpus_tools/annis/gui/graphml/DocumentGraphMapper.java @@ -28,7 +28,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.corpus_tools.annis.api.model.AnnotationComponentType; import org.corpus_tools.annis.api.model.Component; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.SALT_TYPE; import org.corpus_tools.salt.SaltFactory; import org.corpus_tools.salt.common.SDocumentGraph; diff --git a/src/main/java/org/corpus_tools/annis/gui/media/MediaController.java b/src/main/java/org/corpus_tools/annis/gui/media/MediaController.java index dc16fcedf2..88f8bfcac0 100644 --- a/src/main/java/org/corpus_tools/annis/gui/media/MediaController.java +++ b/src/main/java/org/corpus_tools/annis/gui/media/MediaController.java @@ -13,7 +13,7 @@ */ package org.corpus_tools.annis.gui.media; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; /** * System wide access to multimedia player functions. @@ -30,7 +30,7 @@ public interface MediaController { * @param player The instance. * @param resultID To which result this player belongs. */ - public void addMediaPlayer(MediaPlayer player, String resultID, VisualizationToggle toggle); + public void addMediaPlayer(MediaPlayer player, String resultID, VisualizerPanel visPanel); /** * Unregister all associated {@link MediaPlayer} instances. diff --git a/src/main/java/org/corpus_tools/annis/gui/media/MediaControllerImpl.java b/src/main/java/org/corpus_tools/annis/gui/media/MediaControllerImpl.java index 8e4034b157..2d72ee894f 100644 --- a/src/main/java/org/corpus_tools/annis/gui/media/MediaControllerImpl.java +++ b/src/main/java/org/corpus_tools/annis/gui/media/MediaControllerImpl.java @@ -22,7 +22,7 @@ import java.util.TreeMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.LoadableVisualizer; import org.springframework.stereotype.Component; @@ -76,9 +76,9 @@ public void visualizerLoaded(LoadableVisualizer origin) { */ private Map lastUsedPlayer; - private Map visToggle; + private Map visPanels; - /** Since everone can call us asynchronously we need a locking mechanism */ + /** Since everyone can call us asynchronously we need a locking mechanism */ private final ReadWriteLock lock = new ReentrantReadWriteLock(); public MediaControllerImpl() { @@ -86,14 +86,14 @@ public MediaControllerImpl() { try { mediaPlayers = new TreeMap>(); lastUsedPlayer = new TreeMap(); - visToggle = new HashMap(); + visPanels = new HashMap<>(); } finally { lock.writeLock().unlock(); } } @Override - public void addMediaPlayer(MediaPlayer player, String resultID, VisualizationToggle toggle) { + public void addMediaPlayer(MediaPlayer player, String resultID, VisualizerPanel visPanel) { // some sanity checks if (resultID == null) { return; @@ -110,7 +110,7 @@ public void addMediaPlayer(MediaPlayer player, String resultID, VisualizationTog List playerList = mediaPlayers.get(resultID); playerList.add(player); - visToggle.put(player, toggle); + visPanels.put(player, visPanel); } finally { lock.writeLock().unlock(); } @@ -121,7 +121,7 @@ public void clearMediaPlayers() { lock.writeLock().lock(); try { mediaPlayers.clear(); - visToggle.clear(); + visPanels.clear(); lastUsedPlayer.clear(); } finally { lock.writeLock().unlock(); @@ -133,9 +133,9 @@ public void closeOtherPlayers(MediaPlayer doNotCloseThisOne) { for (List playersForID : mediaPlayers.values()) { for (MediaPlayer player : playersForID) { if (player != doNotCloseThisOne) { - VisualizationToggle t = visToggle.get(player); - if (t != null) { - t.toggleVisualizer(false, null); + VisualizerPanel p = visPanels.get(player); + if (p != null) { + p.toggleVisualizer(false, null); } } } @@ -170,10 +170,10 @@ public void play(String resultID, double startTime) { if (player != null) { closeOtherPlayers(player); - VisualizationToggle t = visToggle.get(player); - if (t != null) { + VisualizerPanel p = visPanels.get(player); + if (p != null) { foundPlayer = true; - t.toggleVisualizer(true, new CallbackImpl(player, startTime, null)); + p.toggleVisualizer(true, new CallbackImpl(player, startTime, null)); } } @@ -198,10 +198,10 @@ public void play(String resultID, double startTime, double endTime) { if (player != null) { closeOtherPlayers(player); - VisualizationToggle t = visToggle.get(player); - if (t != null) { + VisualizerPanel p = visPanels.get(player); + if (p != null) { foundPlayer = true; - t.toggleVisualizer(true, new CallbackImpl(player, startTime, endTime)); + p.toggleVisualizer(true, new CallbackImpl(player, startTime, endTime)); } } } finally { diff --git a/src/main/java/org/corpus_tools/annis/gui/objects/CorpusConfigMap.java b/src/main/java/org/corpus_tools/annis/gui/objects/CorpusConfigMap.java deleted file mode 100644 index 765967b1a6..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/objects/CorpusConfigMap.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2013 SFB 632. - * - * 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.corpus_tools.annis.gui.objects; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import javax.xml.bind.annotation.XmlRootElement; -import org.corpus_tools.annis.api.model.CorpusConfiguration; - -/** - * Maps corpus names to corpus configurations. - * - * @author Benjamin Weißenfels {@literal } - */ -@XmlRootElement -public class CorpusConfigMap implements Serializable { - - /** - * - */ - private static final long serialVersionUID = -7045818041525225347L; - private Map corpusConfigs = new HashMap<>(); - - /** - * Checks if a corpus configuration is defined for a specific corpus name. - * - * @param corpusName The corpus name, for which the config is lookup. - * @return True if corpus configuration is defined for this corpus. - */ - public boolean containsConfig(String corpusName) { - if (corpusConfigs != null) { - return corpusConfigs.containsKey(corpusName); - } - - return false; - } - - public CorpusConfiguration get(String k) { - return corpusConfigs.get(k); - } - - /** - * @return the corpusConfigs - */ - public Map getCorpusConfigs() { - return (corpusConfigs == null) ? new HashMap<>() : corpusConfigs; - } - - public boolean isEmpty() { - return corpusConfigs == null || corpusConfigs.isEmpty(); - } - - public CorpusConfiguration put(String k, CorpusConfiguration corpusConfig) { - return corpusConfigs.put(k, corpusConfig); - } - - /** - * @param corpusConfigs the corpusConfigs to set - */ - public void setCorpusConfigs(Map corpusConfigs) { - this.corpusConfigs = corpusConfigs; - } -} diff --git a/src/main/java/org/corpus_tools/annis/gui/CorpusSet.java b/src/main/java/org/corpus_tools/annis/gui/objects/CorpusSet.java similarity index 97% rename from src/main/java/org/corpus_tools/annis/gui/CorpusSet.java rename to src/main/java/org/corpus_tools/annis/gui/objects/CorpusSet.java index 788d225972..1f17f0b1c3 100644 --- a/src/main/java/org/corpus_tools/annis/gui/CorpusSet.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/CorpusSet.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.objects; import java.io.Serializable; import java.util.Set; diff --git a/src/main/java/org/corpus_tools/annis/gui/objects/ExportQueryGenerator.java b/src/main/java/org/corpus_tools/annis/gui/objects/ExportQueryGenerator.java index 8880107479..143faacc8a 100644 --- a/src/main/java/org/corpus_tools/annis/gui/objects/ExportQueryGenerator.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/ExportQueryGenerator.java @@ -1,8 +1,8 @@ package org.corpus_tools.annis.gui.objects; import java.util.List; -import org.corpus_tools.annis.gui.QueryGenerator.ContextQueryGenerator; import org.corpus_tools.annis.gui.exporter.ExporterPlugin; +import org.corpus_tools.annis.gui.objects.QueryGenerator.ContextQueryGenerator; public class ExportQueryGenerator extends ContextQueryGenerator { public ExportQueryGenerator() { diff --git a/src/main/java/org/corpus_tools/annis/gui/FontConfig.java b/src/main/java/org/corpus_tools/annis/gui/objects/FontConfig.java similarity index 97% rename from src/main/java/org/corpus_tools/annis/gui/FontConfig.java rename to src/main/java/org/corpus_tools/annis/gui/objects/FontConfig.java index a6079ceac0..809e13a4e0 100644 --- a/src/main/java/org/corpus_tools/annis/gui/FontConfig.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/FontConfig.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.objects; import java.io.Serializable; import javax.xml.bind.annotation.XmlElement; diff --git a/src/main/java/org/corpus_tools/annis/gui/InstanceConfig.java b/src/main/java/org/corpus_tools/annis/gui/objects/InstanceConfig.java similarity index 99% rename from src/main/java/org/corpus_tools/annis/gui/InstanceConfig.java rename to src/main/java/org/corpus_tools/annis/gui/objects/InstanceConfig.java index 3b120c2378..d8fc0fd484 100644 --- a/src/main/java/org/corpus_tools/annis/gui/InstanceConfig.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/InstanceConfig.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.objects; import java.io.Serializable; import java.util.LinkedList; diff --git a/src/main/java/org/corpus_tools/annis/gui/objects/Match.java b/src/main/java/org/corpus_tools/annis/gui/objects/Match.java index 8bd02323fc..e7291b47cb 100644 --- a/src/main/java/org/corpus_tools/annis/gui/objects/Match.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/Match.java @@ -27,7 +27,7 @@ import javax.xml.bind.annotation.XmlAccessorOrder; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; /** * Represents a single match of an AQL query. diff --git a/src/main/java/org/corpus_tools/annis/gui/MatchedNodeColors.java b/src/main/java/org/corpus_tools/annis/gui/objects/MatchedNodeColors.java similarity index 98% rename from src/main/java/org/corpus_tools/annis/gui/MatchedNodeColors.java rename to src/main/java/org/corpus_tools/annis/gui/objects/MatchedNodeColors.java index 89cac19ce2..ee049d53e4 100644 --- a/src/main/java/org/corpus_tools/annis/gui/MatchedNodeColors.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/MatchedNodeColors.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.objects; import java.awt.Color; import java.util.Locale; diff --git a/src/main/java/org/corpus_tools/annis/gui/objects/Query.java b/src/main/java/org/corpus_tools/annis/gui/objects/Query.java index c4d5832c3d..65ff703a55 100644 --- a/src/main/java/org/corpus_tools/annis/gui/objects/Query.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/Query.java @@ -27,7 +27,7 @@ import java.util.Objects; import java.util.Set; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; /** * A POJO representing a query. diff --git a/src/main/java/org/corpus_tools/annis/gui/QueryGenerator.java b/src/main/java/org/corpus_tools/annis/gui/objects/QueryGenerator.java similarity index 89% rename from src/main/java/org/corpus_tools/annis/gui/QueryGenerator.java rename to src/main/java/org/corpus_tools/annis/gui/objects/QueryGenerator.java index 524b27cc64..c01be38051 100644 --- a/src/main/java/org/corpus_tools/annis/gui/QueryGenerator.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/QueryGenerator.java @@ -11,19 +11,12 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.objects; import java.util.LinkedHashSet; import java.util.Set; import java.util.TreeSet; import org.corpus_tools.annis.api.model.FindQuery.OrderEnum; -import org.corpus_tools.annis.gui.objects.ContextualizedQuery; -import org.corpus_tools.annis.gui.objects.DisplayedResultQuery; -import org.corpus_tools.annis.gui.objects.FrequencyQuery; -import org.corpus_tools.annis.gui.objects.FrequencyTableQuery; -import org.corpus_tools.annis.gui.objects.PagedResultQuery; -import org.corpus_tools.annis.gui.objects.Query; -import org.corpus_tools.annis.gui.objects.QueryLanguage; /** * Helper class to construct new {@link Query} objects (or one of the child classes) diff --git a/src/main/java/org/corpus_tools/annis/gui/objects/QueryUIState.java b/src/main/java/org/corpus_tools/annis/gui/objects/QueryUIState.java index 783e5a5b9d..3fb8f37f08 100644 --- a/src/main/java/org/corpus_tools/annis/gui/objects/QueryUIState.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/QueryUIState.java @@ -26,7 +26,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Future; -import okhttp3.Call; import org.corpus_tools.annis.api.model.FindQuery.OrderEnum; import org.corpus_tools.annis.api.model.QueryLanguage; import org.corpus_tools.annis.gui.exporter.CSVExporter; @@ -34,6 +33,7 @@ import org.corpus_tools.annis.gui.frequency.UserGeneratedFrequencyEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import reactor.core.Disposable; /** * Helper class to bundle all query relevant state information of the UI. @@ -79,7 +79,7 @@ public enum QueryType { private transient Map> executedTasks; - private transient Map executedCalls; + private transient Map executedCalls; private final BeanContainer frequencyTableDefinition = new BeanContainer<>(UserGeneratedFrequencyEntry.class); @@ -110,7 +110,7 @@ public Map> getExecutedTasks() { return executedTasks; } - public Map getExecutedCalls() { + public Map getExecutedCalls() { return executedCalls; } diff --git a/src/main/java/org/corpus_tools/annis/gui/SidebarState.java b/src/main/java/org/corpus_tools/annis/gui/objects/SidebarState.java similarity index 96% rename from src/main/java/org/corpus_tools/annis/gui/SidebarState.java rename to src/main/java/org/corpus_tools/annis/gui/objects/SidebarState.java index 36604f6606..132d7e5c84 100644 --- a/src/main/java/org/corpus_tools/annis/gui/SidebarState.java +++ b/src/main/java/org/corpus_tools/annis/gui/objects/SidebarState.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.objects; import com.vaadin.server.Resource; import org.corpus_tools.annis.gui.util.ANNISFontIcon; diff --git a/src/main/java/org/corpus_tools/annis/gui/paging/PagingCallback.java b/src/main/java/org/corpus_tools/annis/gui/paging/PagingCallback.java deleted file mode 100644 index bc58aebd16..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/paging/PagingCallback.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011 Corpuslinguistic working group Humboldt University Berlin. - * - * 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.corpus_tools.annis.gui.paging; - -import java.io.Serializable; - -/** - * - * @author thomas - */ -public interface PagingCallback extends Serializable { - public void switchPage(long offset, int limit); -} diff --git a/src/main/java/org/corpus_tools/annis/gui/CitationLinkGenerator.java b/src/main/java/org/corpus_tools/annis/gui/query_references/CitationLinkGenerator.java similarity index 96% rename from src/main/java/org/corpus_tools/annis/gui/CitationLinkGenerator.java rename to src/main/java/org/corpus_tools/annis/gui/query_references/CitationLinkGenerator.java index 6238c77ce2..c8e34e71d7 100644 --- a/src/main/java/org/corpus_tools/annis/gui/CitationLinkGenerator.java +++ b/src/main/java/org/corpus_tools/annis/gui/query_references/CitationLinkGenerator.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.query_references; import com.vaadin.icons.VaadinIcons; import com.vaadin.ui.Button; @@ -22,11 +22,11 @@ import com.vaadin.v7.ui.Table; import java.util.HashSet; import java.util.Set; -import org.corpus_tools.annis.gui.beans.CitationProvider; +import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.objects.ContextualizedQuery; import org.corpus_tools.annis.gui.objects.DisplayedResultQuery; import org.corpus_tools.annis.gui.objects.Query; -import org.corpus_tools.annis.gui.query_references.ShareQueryReferenceWindow; +import org.corpus_tools.annis.gui.objects.QueryGenerator; /** * diff --git a/src/main/java/org/corpus_tools/annis/gui/beans/CitationProvider.java b/src/main/java/org/corpus_tools/annis/gui/query_references/CitationProvider.java similarity index 94% rename from src/main/java/org/corpus_tools/annis/gui/beans/CitationProvider.java rename to src/main/java/org/corpus_tools/annis/gui/query_references/CitationProvider.java index 0f7dd9f9f9..43d2db0c92 100644 --- a/src/main/java/org/corpus_tools/annis/gui/beans/CitationProvider.java +++ b/src/main/java/org/corpus_tools/annis/gui/query_references/CitationProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.beans; +package org.corpus_tools.annis.gui.query_references; import java.util.Set; diff --git a/src/main/java/org/corpus_tools/annis/gui/query_references/UrlShortener.java b/src/main/java/org/corpus_tools/annis/gui/query_references/UrlShortener.java index e622e53b7c..ca7d44c90e 100644 --- a/src/main/java/org/corpus_tools/annis/gui/query_references/UrlShortener.java +++ b/src/main/java/org/corpus_tools/annis/gui/query_references/UrlShortener.java @@ -8,7 +8,7 @@ import java.util.UUID; import javax.transaction.Transactional; import org.corpus_tools.annis.gui.CommonUI; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Service; @@ -46,7 +46,7 @@ public String shortenURL(URI original, CommonUI ui) { UrlShortenerEntry entry = new UrlShortenerEntry(); entry.setId(UUID.randomUUID()); entry.setUrl(localURL); - Optional user = Helper.getUser(ui); + Optional user = Helper.getUser(); if (user.isPresent()) { entry.setOwner(Helper.getDisplayName(user.get())); } else { diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/AddMenu.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/AddMenu.java similarity index 97% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/AddMenu.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/AddMenu.java index dee58d6d98..3edd77e42c 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/AddMenu.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/AddMenu.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.MenuItem; diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/EdgeBox.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/EdgeBox.java similarity index 98% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/EdgeBox.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/EdgeBox.java index 9f5bff6bcf..1f7143d873 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/EdgeBox.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/EdgeBox.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import com.vaadin.ui.Panel; import com.vaadin.v7.ui.AbstractSelect; diff --git a/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/FlatQueryBuilder.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/FlatQueryBuilder.java new file mode 100644 index 0000000000..1cfd18fca7 --- /dev/null +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/FlatQueryBuilder.java @@ -0,0 +1,669 @@ + +/* + * Copyright 2013 Corpuslinguistic working group Humboldt University Berlin. + * + * 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.corpus_tools.annis.gui.querybuilder.flat; + +import com.vaadin.data.Binder; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.MenuBar; +import com.vaadin.ui.MenuBar.MenuItem; +import com.vaadin.ui.Notification; +import com.vaadin.ui.Panel; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.v7.ui.NativeSelect; +import com.vaadin.v7.ui.themes.ChameleonTheme; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import org.corpus_tools.annis.api.model.AnnoKey; +import org.corpus_tools.annis.api.model.Annotation; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.controller.QueryController; +import org.corpus_tools.annis.gui.objects.Query; +import org.corpus_tools.annis.gui.objects.QueryLanguage; +import org.corpus_tools.annis.gui.objects.QueryUIState; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.annis.gui.util.IDGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/* + * @author martin klotz (martin.klotz@hu-berlin.de) + * + * @author tom ruette (tom.ruette@hu-berlin.de) + */ +public class FlatQueryBuilder extends Panel implements Button.ClickListener { + + /** + * + */ + private static final long serialVersionUID = -1659782316940380300L; + private static final Logger log = LoggerFactory.getLogger(FlatQueryBuilder.class); + + private static final String[] REGEX_CHARACTERS = + {"\\", "+", ".", "[", "*", "^", "$", "|", "?", "(", ")"}; + private static final String BUTTON_GO_LABEL = "Create AQL Query"; + private static final String BUTTON_CLEAR_LABEL = "Clear the Query Builder"; + private static final String NO_CORPORA_WARNING = + "No corpora selected, please select " + "at least one corpus."; + private static final String INCOMPLETE_QUERY_WARNING = "Query seems to be incomplete."; + private static final String ADD_LING_PARAM = "Add"; + private static final String ADD_SPAN_PARAM = "Add"; + private static final String CHANGE_SPAN_PARAM = "Change"; + private static final String ADD_META_PARAM = "Add"; + private static final String INFO_INIT_LANG = "In this part of the Query Builder, " + + "blocks of the linguistic query can be constructed from left to right."; + private static final String INFO_INIT_SPAN = "This part of the Query Builder " + + "allows you to define a span annotation within which the above query blocks " + + "are confined."; + private static final String INFO_INIT_META = + "Here, you can constrain the linguistic " + "query by selecting meta levels."; + private static final String INFO_FILTER = "When searching in the fields, the " + + "hits are sorted and filtered according to different mechanisms. Please " + + "choose a filtering mechanism here."; + private static final String TOOLBAR_CAPTION = "Toolbar"; + private static final String META_CAPTION = "Meta information"; + + private static final String SPAN_CAPTION = "Scope"; + + private static final String LANG_CAPTION = "Linguistic sequence"; + private static final String ADVANCED_CAPTION = "Advanced settings"; + private Button btGo; + private Button btClear; + + private Button btInitSpan; + private Button btInitMeta; + private Button btInitLanguage; + private MenuBar addMenu; + + private MenuBar addMenuSpan; + private MenuBar addMenuMeta; + private QueryController cp; + private HorizontalLayout language; + + private HorizontalLayout languagenodes; + private HorizontalLayout span; + private HorizontalLayout meta; + private HorizontalLayout toolbar; + private HorizontalLayout advanced; + + private VerticalLayout mainLayout; + + private NativeSelect filtering; + + private Collection vnodes; + + private Collection eboxes; + + private Collection mboxes; + + private SpanBox spbox; + + private MenuBar.MenuItem spanMenu; + + private ReducingStringComparator rsc; + + public FlatQueryBuilder(QueryController cp) { + setSizeFull(); + launch(cp); + + } + + public void addLinguisticSequenceBox(String annoName) { + if (!vnodes.isEmpty()) { + EdgeBox eb = new EdgeBox(this); + languagenodes.addComponent(eb); + eboxes.add(eb); + } + VerticalNode vn = new VerticalNode(annoName, this); + languagenodes.addComponent(vn); + vnodes.add(vn); + addMenu.setAutoOpen(false); + } + + + void addSpanBox(String level) { + spbox = new SpanBox(level, this); + span.addComponent(spbox); + span.setComponentAlignment(spbox, Alignment.MIDDLE_LEFT); + spanMenu.setText(CHANGE_SPAN_PARAM); + } + + void addMetaBox(String annoname) { + MetaBox mb = new MetaBox(annoname, this); + meta.addComponent(mb); + mboxes.add(mb); + addMenuMeta.setAutoOpen(false); + for (MenuItem textItem : addMenuMeta.getItems()) { + if ("Add".equals(textItem.getText())) { + for (MenuItem item : textItem.getChildren()) { + if (annoname.equals(item.getText())) { + item.setVisible(false); + } + } + } + } + } + + @Override + public void attach() { + super.attach(); + + Binder binder = new Binder<>(); + binder.addValueChangeListener(e -> this.initialize()); + binder.setBean(cp.getState()); + + IDGenerator.assignIDForFields(this, language, btInitLanguage, btGo); + } + + @Override + public void buttonClick(Button.ClickEvent event) { + if (cp.getState().getSelectedCorpora().isEmpty()) { + Notification.show(NO_CORPORA_WARNING); + } else { + if (event.getButton() == btGo) { + updateQuery(); + } + + if (event.getButton() == btClear) { + clear(); + updateQuery(); + launch(cp); + } + + if (event.getComponent() == btInitMeta || event.getComponent() == btInitSpan + || event.getComponent() == btInitLanguage) { + initialize(); + } + } + } + + private void clear() + // check whether it is necessary to do this in a method + { + language.removeAllComponents(); + span.removeAllComponents(); + meta.removeAllComponents(); + toolbar.removeAllComponents(); + mainLayout.removeComponent(language); + mainLayout.removeComponent(span); + mainLayout.removeComponent(meta); + mainLayout.removeComponent(toolbar); + vnodes.clear(); + eboxes.clear(); + mboxes.clear(); + } + + public String escapeRegexCharacters(String tok) { + if (tok == null) { + return ""; + } + if (tok.equals("")) { + return ""; + } + String result = tok; + for (int i = 0; i < REGEX_CHARACTERS.length; i++) { + result = result.replace(REGEX_CHARACTERS[i], "\\" + REGEX_CHARACTERS[i]); + } + + return result.replace("/", "\\x2F"); + } + + public Collection getAnnotationValues(String level) { + Collection values = new TreeSet<>(); + for (String s : getAvailableAnnotationLevels(level)) { + values.add(s); + } + return values; + } + + private String getAQLFragment(SearchBox sb) { + String result = ""; + String value = null; + try { + value = sb.getValue(); + } catch (java.lang.NullPointerException ex) { + value = null; + } + String level = sb.getAttribute(); + if (value == null) { + result = level; + } + if (value != null) { + if (sb.isRegEx() && !sb.isNegativeSearch()) { + result = level + "=/" + value.replace("/", "\\x2F") + "/"; + } + if (sb.isRegEx() && sb.isNegativeSearch()) { + result = level + "!=/" + value.replace("/", "\\x2F") + "/"; + } + if (!sb.isRegEx() && sb.isNegativeSearch()) { + result = level + "!=\"" + value.replace("\"", "\\x22") + "\""; + } + if (!sb.isRegEx() && !sb.isNegativeSearch()) { + result = level + "=\"" + value.replace("\"", "\\x22") + "\""; + } + } + return result; + } + + private String getAQLQuery() { + int count = 1; + StringBuilder ql = new StringBuilder(); + StringBuilder edgeQuery = new StringBuilder(); + StringBuilder sentenceQuery = new StringBuilder(); + Collection sentenceVars = new ArrayList<>(); + Iterator itEboxes = eboxes.iterator(); + for (VerticalNode v : vnodes) { + Collection sboxes = v.getSearchBoxes(); + for (SearchBox s : sboxes) { + ql.append(" & " + getAQLFragment(s)); + } + if (sboxes.isEmpty()) { + // not sure we want to do it this way: + ql.append("\n& /.*/"); + } + sentenceVars.add(Integer.valueOf(count)); + for (int i = 1; i < sboxes.size(); i++) { + String addQuery = "\n& #" + count + "_=_" + "#" + ++count; + edgeQuery.append(addQuery); + } + count++; + String edgeQueryAdds = (itEboxes.hasNext()) + ? "\n& #" + (count - 1) + " " + itEboxes.next().getValue() + " #" + count + : ""; + edgeQuery.append(edgeQueryAdds); + } + String addQuery = ""; + try { + SpanBox spb = (SpanBox) span.getComponent(1); + if ((!spb.isRegEx()) && (!spb.getValue().isEmpty())) { + addQuery = "\n& " + spb.getAttribute() + " = \"" + spb.getValue() + "\""; + } + if (spb.isRegEx()) { + addQuery = + "\n& " + spb.getAttribute() + " = /" + spb.getValue().replace("/", "\\x2F") + "/"; + } + if (spb.getValue().isEmpty()) { + addQuery = "\n&" + spb.getAttribute(); + } + ql.append(addQuery); + for (Integer i : sentenceVars) { + sentenceQuery.append("\n& #").append(count).append("_i_#").append(i.toString()); + } + } catch (Exception ex) { + // Ignore this exception + } + StringBuilder metaQuery = new StringBuilder(); + Iterator itMetaBoxes = mboxes.iterator(); + while (itMetaBoxes.hasNext()) { + metaQuery.append(getMetaQueryFragment(itMetaBoxes.next())); + } + String fullQuery = + (ql.toString() + edgeQuery.toString() + sentenceQuery.toString() + metaQuery.toString()); + if (fullQuery.length() < 3) { + return ""; + } + fullQuery = fullQuery.substring(3);// deletes leading " & " + return fullQuery; + } + + public Collection getAvailableAnnotationLevels(String meta) { + Collection result = new TreeSet<>(); + UI ui = UI.getCurrent(); + if (ui instanceof CommonUI) { + WebClient client = ((CommonUI) ui).getWebClient(); + // get current corpus selection + Collection corpusSelection = cp.getState().getSelectedCorpora(); + try { + List atts = new LinkedList<>(); + for (String corpus : corpusSelection) { + List allAnnotations = client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/node-annotations") + .queryParam("list_values", true).queryParam("only_most_frequent_values", false) + .build(corpus)) + .retrieve().bodyToFlux(Annotation.class).collectList().block(); + atts.addAll(allAnnotations); + } + for (Annotation a : atts) { + if (a.getKey().getName().equals(meta)) { + result.add(a.getVal()); + } + } + } catch (WebClientResponseException ex) { + log.error(null, ex); + } + } + return result; + } + + public Set getAvailableAnnotationNames() { + Set result = new TreeSet<>(); + // get current corpus selection + Collection corpusSelection = cp.getState().getSelectedCorpora(); + UI ui = UI.getCurrent(); + if (ui instanceof CommonUI) { + WebClient client = ((CommonUI) ui).getWebClient(); + try { + for (String corpus : corpusSelection) { + Iterable allAnnotations = client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/node-annotations") + .queryParam("list_values", false).queryParam("only_most_frequent_values", false) + .build(corpus)) + .retrieve().bodyToFlux(Annotation.class).toIterable(); + for (Annotation a : allAnnotations) { + result.add(a.getKey().getName()); + } + } + } catch (WebClientResponseException ex) { + log.error(null, ex); + } + } + + result.add("tok"); + return result; + } + + public Set getAvailableMetaNames() { + Set result = new TreeSet<>(); + if (UI.getCurrent() instanceof CommonUI) { + WebClient client = ((CommonUI) UI.getCurrent()).getWebClient(); + // get current corpus selection + Collection corpusSelection = cp.getState().getSelectedCorpora(); + try { + for (String corpus : corpusSelection) { + for (AnnoKey key : Helper.getMetaAnnotationNames(corpus, client).toIterable()) { + result.add(key.getName()); + } + } + + } catch (WebClientResponseException ex) { + log.error(null, ex); + } + } + + + return result; + } + + public String getFilterMechanism() { + return filtering.getItemCaption(filtering.getValue()); + } + + private String getMetaQueryFragment(MetaBox mb) { + Collection values = mb.getValues(); + if (!values.isEmpty()) { + StringBuilder result = new StringBuilder("\n& meta::" + mb.getMetaDatum() + " = "); + if (values.size() == 1) { + result.append("\"" + values.iterator().next().replace("\"", "\\x22") + "\""); + } else { + Iterator itValues = values.iterator(); + result.append("/(" + escapeRegexCharacters(itValues.next()) + ")"); + while (itValues.hasNext()) { + result.append("|(" + escapeRegexCharacters(itValues.next()) + ")"); + } + result.append("/"); + } + return result.toString(); + } + return ""; + } + + public ReducingStringComparator getRSC() { + return rsc; + } + + private void initialize() { + // try to remove all existing menus + try { + if (addMenu != null) { + language.removeComponent(addMenu); + } + if (addMenuSpan != null) { + span.removeComponent(addMenuSpan); + } + if (addMenuMeta != null) { + meta.removeComponent(addMenuMeta); + } + } catch (Exception e) { + log.error(null, e); + } + + // init variables: + final FlatQueryBuilder sq = this; + Collection annonames = getAvailableAnnotationNames(); + Collection metanames = getAvailableMetaNames(); + + // Code from btInitLanguage: + addMenu = new MenuBar(); + // addMenu.setDescription(INFO_INIT_LANG); + addMenu.setAutoOpen(false); + final MenuBar.MenuItem add = addMenu.addItem(ADD_LING_PARAM, null); + for (final String annoname : annonames) { + add.addItem(annoname, selectedItem -> addLinguisticSequenceBox(annoname)); + } + language.removeComponent(btInitLanguage); + language.addComponent(addMenu); + + // Code from btInitSpan: + addMenuSpan = new MenuBar(); + // addMenuSpan.setDescription(INFO_INIT_SPAN); + addMenuSpan.setAutoOpen(false); + final MenuBar.MenuItem addSpan = addMenuSpan.addItem(ADD_SPAN_PARAM, null); + for (final String annoname : annonames) { + addSpan.addItem(annoname, selectedItem -> { + sq.removeSpanBox(); + sq.addSpanBox(annoname); + addMenuSpan.setAutoOpen(false); + }); + } + spanMenu = addSpan; + span.removeComponent(btInitSpan); + span.addComponent(addMenuSpan); + + // Code from btInitMeta: + addMenuMeta = new MenuBar(); + // addMenuMeta.setDescription(INFO_INIT_META); + addMenuMeta.setAutoOpen(false); + final MenuBar.MenuItem addMeta = addMenuMeta.addItem(ADD_META_PARAM, null); + for (final String annoname : metanames) { + addMeta.addItem(annoname, selectedItem -> addMetaBox(annoname)); + } + meta.removeComponent(btInitMeta); + meta.addComponent(addMenuMeta); + } + + private void launch(QueryController cp) { + this.cp = cp; + rsc = new ReducingStringComparator(); + mainLayout = new VerticalLayout(); + // tracking lists for vertical nodes, edgeboxes and metaboxes + vnodes = new ArrayList<>(); + eboxes = new ArrayList<>(); + mboxes = new ArrayList<>(); + spbox = null; + // buttons and checks + btGo = new Button(BUTTON_GO_LABEL, this); + btGo.setStyleName(ChameleonTheme.BUTTON_SMALL); + btClear = new Button(BUTTON_CLEAR_LABEL, this); + btClear.setStyleName(ChameleonTheme.BUTTON_SMALL); + btInitLanguage = new Button("Initialize", this); + btInitLanguage.setDescription(INFO_INIT_LANG); + btInitSpan = new Button("Initialize", this); + btInitSpan.setDescription(INFO_INIT_SPAN); + btInitMeta = new Button("Initialize", this); + btInitMeta.setDescription(INFO_INIT_META); + filtering = new NativeSelect("Filtering mechanisms"); + filtering.setDescription(INFO_FILTER); + ReducingStringComparator rdc = new ReducingStringComparator(); + Set mappings = rdc.getMappings().keySet(); + int i; + for (i = 0; i < mappings.size(); i++) { + String mapname = (String) mappings.toArray()[i]; + filtering.addItem(i); + filtering.setItemCaption(i, mapname); + } + filtering.addItem(i + 1); + filtering.setItemCaption(i + 1, "generic"); + filtering.select(i + 1); + filtering.setNullSelectionAllowed(false); + filtering.setImmediate(true); + // language layout + language = new HorizontalLayout(); + languagenodes = new HorizontalLayout(); + language.addComponent(languagenodes); + language.addComponent(btInitLanguage); + language.setMargin(true); + language.setCaption(LANG_CAPTION); + language.addStyleName("linguistics-panel"); + // span layout + span = new HorizontalLayout(); + span.setSpacing(true); + span.addComponent(btInitSpan); + span.setMargin(true); + span.setCaption(SPAN_CAPTION); + span.addStyleName("span-panel"); + // meta layout + meta = new HorizontalLayout(); + meta.setSpacing(true); + meta.addComponent(btInitMeta); + meta.setMargin(true); + meta.setCaption(META_CAPTION); + meta.addStyleName("meta-panel"); + // toolbar layout + toolbar = new HorizontalLayout(); + toolbar.setSpacing(true); + toolbar.addComponent(btGo); + toolbar.addComponent(btClear); + toolbar.setMargin(true); + toolbar.setCaption(TOOLBAR_CAPTION); + toolbar.addStyleName("toolbar-panel"); + // advanced + advanced = new HorizontalLayout(); + advanced.setSpacing(true); + advanced.addComponent(filtering); + advanced.setMargin(true); + advanced.setCaption(ADVANCED_CAPTION); + advanced.addStyleName("advanced-panel"); + // put everything on the layout + mainLayout.setSpacing(true); + mainLayout.addComponent(language); + mainLayout.addComponent(span); + mainLayout.addComponent(meta); + mainLayout.addComponent(toolbar); + mainLayout.addComponent(advanced); + setContent(mainLayout); + getContent().setWidth("100%"); + getContent().setHeight("-1px"); + } + + public void removeMetaBox(MetaBox v) { + meta.removeComponent(v); + mboxes.remove(v); + List items = addMenuMeta.getItems().get(0).getChildren(); + boolean found = false; + String metalevel = v.getMetaDatum(); + for (int i = 0; (i < items.size()) && !found; i++) { + MenuBar.MenuItem itm = items.get(i); + if (itm.getText().equals(metalevel)) { + itm.setVisible(true); + found = true; + } + } + } + + public void removeSpanBox() { + if (spbox != null) { + span.removeComponent(spbox); + spbox = null; + spanMenu.setText(ADD_SPAN_PARAM); + } + } + + public void removeSpanBox(SpanBox spb) { + if (spb.equals(spbox)) { + removeSpanBox(); + } + } + + public void removeVerticalNode(VerticalNode v) { + Iterator itVnodes = vnodes.iterator(); + Iterator itEboxes = eboxes.iterator(); + VerticalNode vn = itVnodes.next(); + EdgeBox eb = null; + + while (!vn.equals(v)) { + vn = itVnodes.next(); + eb = itEboxes.next(); + } + + if ((eb == null) && (itEboxes.hasNext())) { + eb = itEboxes.next(); + } + + vnodes.remove(v); + if (eb != null) { + eboxes.remove(eb); + languagenodes.removeComponent(eb); + } + languagenodes.removeComponent(v); + } + + + public String unescape(String s) { + // first unescape slashes and quotes: + + s = unescapeSlQ(s); + + // unescape regex characters: + int i = 1; + while (i < s.length()) { + char c0 = s.charAt(i - 1); + char c1 = s.charAt(i); + for (int j = 0; j < REGEX_CHARACTERS.length; j++) { + if ((c1 == REGEX_CHARACTERS[j].charAt(0)) && (c0 == '\\')) { + s = s.substring(0, i - 1) + s.substring(i); + break; + } + if (j == REGEX_CHARACTERS.length - 1) { + i++; + } + } + } + + return s; + } + + public String unescapeSlQ(String s) { + return s.replace("\\x2F", "/").replace("\\x22", "\""); + } + + public void updateQuery() { + try { + cp.setQuery(new Query(getAQLQuery(), QueryLanguage.AQL, + new LinkedHashSet<>(cp.getState().getSelectedCorpora()))); + } catch (java.lang.NullPointerException ex) { + Notification.show(INCOMPLETE_QUERY_WARNING); + } + } +} diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/FlatQueryBuilderPlugin.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/FlatQueryBuilderPlugin.java similarity index 89% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/FlatQueryBuilderPlugin.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/FlatQueryBuilderPlugin.java index eef2945937..ee2543320f 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/FlatQueryBuilderPlugin.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/FlatQueryBuilderPlugin.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; -import org.corpus_tools.annis.gui.QueryController; -import org.corpus_tools.annis.gui.querybuilder.QueryBuilderPlugin; +import org.corpus_tools.annis.gui.controller.QueryController; +import org.corpus_tools.annis.gui.querybuilder.tiger.QueryBuilderPlugin; import org.springframework.stereotype.Component; /** diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/MetaBox.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/MetaBox.java similarity index 96% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/MetaBox.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/MetaBox.java index 98b269b1f9..ea1d275ce7 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/MetaBox.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/MetaBox.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import com.vaadin.ui.Button; import com.vaadin.ui.Panel; @@ -23,7 +23,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.TreeSet; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; /** * @author martin diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/ReducingStringComparator.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/ReducingStringComparator.java similarity index 99% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/ReducingStringComparator.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/ReducingStringComparator.java index 4760fd63e3..0d417cb603 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/ReducingStringComparator.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/ReducingStringComparator.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import com.vaadin.server.ClassResource; import com.vaadin.ui.UI; diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/SearchBox.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/SearchBox.java similarity index 99% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/SearchBox.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/SearchBox.java index 534127902d..2b02cf1ac1 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/SearchBox.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/SearchBox.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/SensitiveComboBox.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/SensitiveComboBox.java similarity index 97% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/SensitiveComboBox.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/SensitiveComboBox.java index 9dadfcbeda..d0b2d11c51 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/SensitiveComboBox.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/SensitiveComboBox.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import com.vaadin.v7.event.FieldEvents.TextChangeEvent; import com.vaadin.v7.event.FieldEvents.TextChangeListener; diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/SpanBox.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/SpanBox.java similarity index 98% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/SpanBox.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/SpanBox.java index 25b90a12ac..64bcdff15b 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/SpanBox.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/SpanBox.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import com.vaadin.ui.Button; import com.vaadin.ui.HorizontalLayout; @@ -35,7 +35,7 @@ import java.util.TreeSet; import java.util.concurrent.ConcurrentSkipListSet; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; /** * @author Tom diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/ValueField.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/ValueField.java similarity index 98% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/ValueField.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/ValueField.java index fdd538d5a2..c7c1b74971 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/ValueField.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/ValueField.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; @@ -37,7 +37,7 @@ import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListSet; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; /** * diff --git a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/VerticalNode.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/VerticalNode.java similarity index 98% rename from src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/VerticalNode.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/VerticalNode.java index d682842659..3706c8676e 100644 --- a/src/main/java/org/corpus_tools/annis/gui/flatquerybuilder/VerticalNode.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/flat/VerticalNode.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; diff --git a/src/main/java/org/corpus_tools/annis/gui/querybuilder/AQLOperator.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/AQLOperator.java similarity index 97% rename from src/main/java/org/corpus_tools/annis/gui/querybuilder/AQLOperator.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/AQLOperator.java index 7d525b4bdb..e83e1ce144 100644 --- a/src/main/java/org/corpus_tools/annis/gui/querybuilder/AQLOperator.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/AQLOperator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.corpus_tools.annis.gui.querybuilder; +package org.corpus_tools.annis.gui.querybuilder.tiger; /** * diff --git a/src/main/java/org/corpus_tools/annis/gui/querybuilder/EdgeWindow.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/EdgeWindow.java similarity index 97% rename from src/main/java/org/corpus_tools/annis/gui/querybuilder/EdgeWindow.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/EdgeWindow.java index 940cb8d667..3960e4698c 100644 --- a/src/main/java/org/corpus_tools/annis/gui/querybuilder/EdgeWindow.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/EdgeWindow.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.querybuilder; +package org.corpus_tools.annis.gui.querybuilder.tiger; import com.vaadin.server.FontAwesome; import com.vaadin.ui.Alignment; @@ -28,7 +28,7 @@ import com.vaadin.v7.data.Property.ValueChangeListener; import com.vaadin.v7.ui.ComboBox; import com.vaadin.v7.ui.TextField; -import org.corpus_tools.annis.gui.querybuilder.NodeWindow.SimpleNewItemHandler; +import org.corpus_tools.annis.gui.querybuilder.tiger.NodeWindow.SimpleNewItemHandler; /** * diff --git a/src/main/java/org/corpus_tools/annis/gui/querybuilder/NodeWindow.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/NodeWindow.java similarity index 99% rename from src/main/java/org/corpus_tools/annis/gui/querybuilder/NodeWindow.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/NodeWindow.java index 237fa3b7dc..08c26ad45e 100644 --- a/src/main/java/org/corpus_tools/annis/gui/querybuilder/NodeWindow.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/NodeWindow.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.querybuilder; +package org.corpus_tools.annis.gui.querybuilder.tiger; import com.vaadin.event.LayoutEvents.LayoutClickEvent; import com.vaadin.event.LayoutEvents.LayoutClickListener; diff --git a/src/main/java/org/corpus_tools/annis/gui/querybuilder/QueryBuilderChooser.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/QueryBuilderChooser.java similarity index 95% rename from src/main/java/org/corpus_tools/annis/gui/querybuilder/QueryBuilderChooser.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/QueryBuilderChooser.java index 8cdc670477..98f3ecdfbe 100644 --- a/src/main/java/org/corpus_tools/annis/gui/querybuilder/QueryBuilderChooser.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/QueryBuilderChooser.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui.querybuilder; +package org.corpus_tools.annis.gui.querybuilder.tiger; import com.vaadin.ui.Component; import com.vaadin.ui.Notification; @@ -24,8 +24,8 @@ import java.util.HashMap; import java.util.Map; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.IDGenerator; -import org.corpus_tools.annis.gui.QueryController; +import org.corpus_tools.annis.gui.controller.QueryController; +import org.corpus_tools.annis.gui.util.IDGenerator; /** * Wrapper for selecting and showing the desired query builder. diff --git a/src/main/java/org/corpus_tools/annis/gui/querybuilder/QueryBuilderPlugin.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/QueryBuilderPlugin.java similarity index 92% rename from src/main/java/org/corpus_tools/annis/gui/querybuilder/QueryBuilderPlugin.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/QueryBuilderPlugin.java index 84c1739c88..dc0f0a9126 100644 --- a/src/main/java/org/corpus_tools/annis/gui/querybuilder/QueryBuilderPlugin.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/QueryBuilderPlugin.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.querybuilder; +package org.corpus_tools.annis.gui.querybuilder.tiger; import com.vaadin.ui.Component; import java.io.Serializable; import org.corpus_tools.annis.gui.AnnisBaseUI; -import org.corpus_tools.annis.gui.QueryController; +import org.corpus_tools.annis.gui.controller.QueryController; /** * Every query builder must implement this interface. It' s also necessary to to diff --git a/src/main/java/org/corpus_tools/annis/gui/querybuilder/TigerQueryBuilderCanvas.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/TigerQueryBuilderCanvas.java similarity index 90% rename from src/main/java/org/corpus_tools/annis/gui/querybuilder/TigerQueryBuilderCanvas.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/TigerQueryBuilderCanvas.java index 1fae58e0d0..1f545ad7f8 100644 --- a/src/main/java/org/corpus_tools/annis/gui/querybuilder/TigerQueryBuilderCanvas.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/TigerQueryBuilderCanvas.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui.querybuilder; +package org.corpus_tools.annis.gui.querybuilder.tiger; import com.vaadin.event.dd.DragAndDropEvent; import com.vaadin.event.dd.DropHandler; @@ -34,14 +34,14 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.api.model.Annotation; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.QueryController; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.controller.QueryController; import org.corpus_tools.annis.gui.widgets.GripDragComponent; import org.corpus_tools.annis.gui.widgets.SimpleCanvas; import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * @@ -312,22 +312,32 @@ public String getAQLQuery() { public Set getAvailableAnnotationNames() { Set result = new TreeSet<>(); - CorporaApi api = new CorporaApi(Helper.getClient(UI.getCurrent())); - - // get current corpus selection - Collection corpusSelection = controller.getState().getSelectedCorpora(); - - if (corpusSelection != null) { - for (String corpus : corpusSelection) { - try { - for (Annotation anno : api.nodeAnnotations(corpus, false, true)) { - result.add(anno.getKey().getName()); + UI ui = UI.getCurrent(); + if (ui instanceof CommonUI) { + WebClient client = ((CommonUI) ui).getWebClient(); + // get current corpus selection + Collection corpusSelection = controller.getState().getSelectedCorpora(); + + if (corpusSelection != null) { + for (String corpus : corpusSelection) { + try { + Iterable allAnnotations = client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/node-annotations") + .queryParam("list_values", false).queryParam("only_most_frequent_values", true) + .build(corpus)) + .retrieve().bodyToFlux(Annotation.class).toIterable(); + for (Annotation anno : allAnnotations) { + result.add(anno.getKey().getName()); + } + } catch (WebClientResponseException ex) { + log.error("Could not get node annotations for corpus " + corpus, ex); } - } catch (ApiException ex) { - log.error("Could not get node annotations for corpus " + corpus, ex); } } + } + + return result; } diff --git a/src/main/java/org/corpus_tools/annis/gui/querybuilder/TigerQueryBuilderPlugin.java b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/TigerQueryBuilderPlugin.java similarity index 98% rename from src/main/java/org/corpus_tools/annis/gui/querybuilder/TigerQueryBuilderPlugin.java rename to src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/TigerQueryBuilderPlugin.java index 1465942c3f..34366afd20 100644 --- a/src/main/java/org/corpus_tools/annis/gui/querybuilder/TigerQueryBuilderPlugin.java +++ b/src/main/java/org/corpus_tools/annis/gui/querybuilder/tiger/TigerQueryBuilderPlugin.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui.querybuilder; +package org.corpus_tools.annis.gui.querybuilder.tiger; import com.vaadin.server.FontAwesome; import com.vaadin.ui.Button; @@ -24,7 +24,7 @@ import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.v7.ui.themes.ChameleonTheme; -import org.corpus_tools.annis.gui.QueryController; +import org.corpus_tools.annis.gui.controller.QueryController; import org.springframework.stereotype.Component; /** diff --git a/src/main/java/org/corpus_tools/annis/gui/requesthandler/BinaryRequestHandler.java b/src/main/java/org/corpus_tools/annis/gui/requesthandler/BinaryRequestHandler.java index ce9f7062e5..ac121f54cf 100644 --- a/src/main/java/org/corpus_tools/annis/gui/requesthandler/BinaryRequestHandler.java +++ b/src/main/java/org/corpus_tools/annis/gui/requesthandler/BinaryRequestHandler.java @@ -21,21 +21,18 @@ import com.vaadin.server.VaadinServletResponse; import com.vaadin.server.VaadinSession; import java.io.IOException; -import java.util.ArrayList; import java.util.Enumeration; -import java.util.HashMap; import java.util.List; import java.util.Map; -import okhttp3.Call; -import okhttp3.Response; import org.apache.commons.io.IOUtils; -import org.corpus_tools.annis.ApiClient; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.Pair; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.gui.CommonUI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * This request handler provides binary-files. It will proxy all requests to the REST service. Since @@ -97,57 +94,36 @@ public void sendResponse(VaadinSession session, VaadinRequest request, // Proxy the whole request, including any HTTP headers (e.g. used for range requests) to the // REST endpoint. - - CorporaApi api = new CorporaApi(ui.getClient()); - ApiClient client = api.getApiClient(); - // create path and map variables - String localVarPath = "/corpora/{corpus}/files/{name}" - .replaceAll("\\{" + "corpus" + "\\}", client.escapeString(toplevelCorpusName)) - .replaceAll("\\{" + "name" + "\\}", client.escapeString(filePath)); - - List localVarQueryParams = new ArrayList(); - List localVarCollectionQueryParams = new ArrayList(); - Map localVarHeaderParams = new HashMap(); - Map localVarCookieParams = new HashMap(); - Map localVarFormParams = new HashMap(); - - Enumeration originalHeaderNames = request.getHeaderNames(); - while (originalHeaderNames.hasMoreElements()) { - String headerName = originalHeaderNames.nextElement(); - String headerValue = request.getHeader(headerName); - localVarHeaderParams.put(headerName, headerValue); - } - - final String[] localVarAccepts = {"default"}; - final String localVarAccept = client.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); - } - - final String[] localVarContentTypes = {}; - final String localVarContentType = client.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - String[] localVarAuthNames = new String[] {"bearerAuth"}; + WebClient client = ui.getWebClient(); // Execute the call and return the response - Call call; try { - call = client.buildCall(localVarPath, "GET", localVarQueryParams, - localVarCollectionQueryParams, null, localVarHeaderParams, localVarCookieParams, - localVarFormParams, localVarAuthNames, null); + ResponseEntity proxyResponse = client.get() + .uri("/corpora/{top}/files/{path}", + toplevelCorpusName, filePath) + .accept(MediaType.ALL).headers(httpHeaders -> { + Enumeration originalHeaderNames = request.getHeaderNames(); + while (originalHeaderNames.hasMoreElements()) { + String headerName = originalHeaderNames.nextElement(); + String headerValue = request.getHeader(headerName); + httpHeaders.set(headerName, headerValue); + } + }).retrieve().toEntity(DataBuffer.class).block(); - Response proxyResponse = call.execute(); // Copy response headers - for (String headerName : proxyResponse.headers().names()) { - response.setHeader(headerName, proxyResponse.header(headerName)); + for (String headerName : proxyResponse.getHeaders().keySet()) { + List headerValues = proxyResponse.getHeaders().getValuesAsList(headerName); + if(!headerValues.isEmpty()) { + response.setHeader(headerName, headerValues.get(0)); + } } // Copy response body - response.setStatus(proxyResponse.code()); - IOUtils.copy(proxyResponse.body().byteStream(), response.getOutputStream()); - } catch (ApiException e) { + response.setStatus(proxyResponse.getStatusCodeValue()); + IOUtils.copy(proxyResponse.getBody().asInputStream(), response.getOutputStream()); + } catch (WebClientResponseException e) { log.error("Could not get binary data from service", e); - response.sendError(e.getCode(), e.getMessage()); + response.sendError(e.getStatusCode().value(), e.getMessage()); } } diff --git a/src/main/java/org/corpus_tools/annis/gui/resultview/ResolverProvider.java b/src/main/java/org/corpus_tools/annis/gui/resultview/ResolverProvider.java deleted file mode 100644 index 553c246155..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/resultview/ResolverProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2011 Corpuslinguistic working group Humboldt University Berlin. - * - * 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.corpus_tools.annis.gui.resultview; - -import com.vaadin.ui.UI; -import java.io.Serializable; -import java.util.List; -import org.corpus_tools.annis.api.model.VisualizerRule; -import org.corpus_tools.salt.common.SDocument; - -/** - * - * @author thomas - */ -public interface ResolverProvider extends Serializable { - public List getResolverEntries(String corpusName, SDocument result, UI ui); - -} diff --git a/src/main/java/org/corpus_tools/annis/gui/resultview/ResolverProviderImpl.java b/src/main/java/org/corpus_tools/annis/gui/resultview/ResolverProviderImpl.java deleted file mode 100644 index f12e687deb..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/resultview/ResolverProviderImpl.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2013 SFB 632. - * - * 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.corpus_tools.annis.gui.resultview; - -import com.vaadin.ui.UI; -import java.io.Serializable; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.corpus_tools.annis.api.model.CorpusConfiguration; -import org.corpus_tools.annis.api.model.VisualizerRule; -import org.corpus_tools.annis.api.model.VisualizerRule.ElementEnum; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.salt.common.SDocument; -import org.corpus_tools.salt.core.SLayer; -import org.corpus_tools.salt.core.SNode; -import org.corpus_tools.salt.core.SRelation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * - * @author Benjamin Weißenfels {@literal } - */ -public class ResolverProviderImpl implements ResolverProvider, Serializable { - - /** - * - */ - private static final long serialVersionUID = 5170826828759716453L; - - private final static Logger log = LoggerFactory.getLogger(ResolverProviderImpl.class); - - private Map, LinkedHashSet> cacheResolver; - - public ResolverProviderImpl( - Map, LinkedHashSet> cacheResolver) { - this.cacheResolver = cacheResolver; - } - - @Override - public List getResolverEntries(String corpusName, SDocument doc, UI ui) { - - - // create a request for resolver entries - HashSet resolverRequests = new HashSet<>(); - - Set nodeLayers = new HashSet(); - Set edgeLayers = new HashSet(); - - if (doc != null && doc.getDocumentGraph() != null) { - for (SNode n : doc.getDocumentGraph().getNodes()) { - for (SLayer layer : n.getLayers()) { - nodeLayers.add(layer.getName()); - } - } - - for (SRelation e : doc.getDocumentGraph().getRelations()) { - for (SLayer layer : e.getLayers()) { - try { - edgeLayers.add(layer.getName()); - } catch (NullPointerException ex) { - log.warn("NullPointerException when using Salt, was trying to get layer name", ex); - } - } - } - - } - - for (String ns : nodeLayers) { - resolverRequests.add(new SingleResolverRequest(corpusName, ns, ElementEnum.NODE)); - } - for (String ns : edgeLayers) { - resolverRequests.add(new SingleResolverRequest(corpusName, ns, ElementEnum.EDGE)); - } - LinkedHashSet matchingRules = new LinkedHashSet<>(); - // query with this resolver request and make sure it is unique - if (cacheResolver.containsKey(resolverRequests)) { - matchingRules.addAll(cacheResolver.get(resolverRequests)); - } else { - - // Get all rules for the corpus in the corpus graph - CorpusConfiguration corpusConfig = Helper.getCorpusConfig(corpusName, ui);; - - if (corpusConfig != null && corpusConfig.getVisualizers() != null) { - for (VisualizerRule visRule : corpusConfig.getVisualizers()) { - for (SingleResolverRequest r : resolverRequests) { - if (visRule.getMappings() == null) { - visRule.setMappings(new LinkedHashMap<>()); - } - if (visRule.getElement() != null && !visRule.getElement().equals(r.getType())) { - continue; - } - if (visRule.getLayer() != null && !visRule.getLayer().equals(r.getNamespace())) { - continue; - } - matchingRules.add(visRule); - } - cacheResolver.put(resolverRequests, matchingRules); - } - } - } - - return new LinkedList<>(matchingRules); - } -} diff --git a/src/main/java/org/corpus_tools/annis/gui/resultview/ResultViewPanel.java b/src/main/java/org/corpus_tools/annis/gui/resultview/ResultViewPanel.java index a16865ce52..1461e5d87f 100644 --- a/src/main/java/org/corpus_tools/annis/gui/resultview/ResultViewPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/resultview/ResultViewPanel.java @@ -27,7 +27,6 @@ import com.vaadin.ui.themes.ValoTheme; import java.io.UnsupportedEncodingException; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -40,16 +39,15 @@ import java.util.TreeMap; import java.util.TreeSet; import org.corpus_tools.annis.api.model.CorpusConfiguration; -import org.corpus_tools.annis.api.model.VisualizerRule; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.IDGenerator; -import org.corpus_tools.annis.gui.QueryController; +import org.corpus_tools.annis.gui.components.PagingComponent; +import org.corpus_tools.annis.gui.controller.QueryController; import org.corpus_tools.annis.gui.controlpanel.QueryPanel; import org.corpus_tools.annis.gui.objects.DisplayedResultQuery; import org.corpus_tools.annis.gui.objects.Match; import org.corpus_tools.annis.gui.objects.PagedResultQuery; -import org.corpus_tools.annis.gui.paging.PagingComponent; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.annis.gui.util.IDGenerator; import org.corpus_tools.salt.SALT_TYPE; import org.corpus_tools.salt.common.SCorpus; import org.corpus_tools.salt.common.SCorpusGraph; @@ -75,31 +73,35 @@ private class MenuBaseTextCommand implements MenuBar.Command { @Override public void menuSelected(MenuItem selectedItem) { - // remember old value - String oldSegmentationLayer = sui.getQueryState().getVisibleBaseText().getValue(); + UI uiRaw = UI.getCurrent(); + if (uiRaw instanceof AnnisUI) { + AnnisUI sui = (AnnisUI) uiRaw; + // remember old value + String oldSegmentationLayer = sui.getQueryState().getVisibleBaseText().getValue(); - // set the new selected item - String newSegmentationLayer = selectedItem.getText(); + // set the new selected item + String newSegmentationLayer = selectedItem.getText(); - if (NULL_SEGMENTATION_VALUE.equals(newSegmentationLayer)) { - newSegmentationLayer = null; - } - for (MenuItem mi : miSegmentation.getChildren()) { - mi.setChecked(mi == selectedItem); - } + if (NULL_SEGMENTATION_VALUE.equals(newSegmentationLayer)) { + newSegmentationLayer = null; + } + for (MenuItem mi : miSegmentation.getChildren()) { + mi.setChecked(mi == selectedItem); + } - if (oldSegmentationLayer != null) { - if (!oldSegmentationLayer.equals(newSegmentationLayer)) { + if (oldSegmentationLayer != null) { + if (!oldSegmentationLayer.equals(newSegmentationLayer)) { + setSegmentationLayer(newSegmentationLayer); + } + } else if (newSegmentationLayer != null) { + // oldSegmentation is null, but selected is not setSegmentationLayer(newSegmentationLayer); } - } else if (newSegmentationLayer != null) { - // oldSegmentation is null, but selected is not - setSegmentationLayer(newSegmentationLayer); - } - // update URL with newly selected segmentation layer - sui.getQueryState().getVisibleBaseText().setValue(newSegmentationLayer); - sui.getSearchView().updateFragment(sui.getQueryController().getSearchQuery()); + // update URL with newly selected segmentation layer + sui.getQueryState().getVisibleBaseText().setValue(newSegmentationLayer); + sui.getSearchView().updateFragment(sui.getQueryController().getSearchQuery()); + } } } @@ -112,8 +114,6 @@ public void menuSelected(MenuItem selectedItem) { private static final String NULL_SEGMENTATION_VALUE = "tokens (default)"; - private final Map, LinkedHashSet> cacheResolver; - private final PagingComponent paging; private final MenuItem miTokAnnos; @@ -141,17 +141,12 @@ public void menuSelected(MenuItem selectedItem) { private final DisplayedResultQuery initialQuery; - private final AnnisUI sui; - public ResultViewPanel(AnnisUI ui, DisplayedResultQuery initialQuery) { - this.sui = ui; + public ResultViewPanel(DisplayedResultQuery initialQuery, QueryController controller) { this.tokenAnnoVisible = new TreeMap<>(); - this.controller = ui.getQueryController(); + this.controller = controller; this.initialQuery = initialQuery; - cacheResolver = Collections.synchronizedMap( - new HashMap, LinkedHashSet>()); - resultPanelList = Collections.synchronizedList(new LinkedList()); resultLayout = new CssLayout(); @@ -189,33 +184,34 @@ public ResultViewPanel(AnnisUI ui, DisplayedResultQuery initialQuery) { setExpandRatio(paging, 0.0f); } - public void addQueryResult(PagedResultQuery q, SaltProject p, List allMatches) { + public void addQueryResult(PagedResultQuery q, SaltProject p, List allMatches, + Map corpusConfigs, AnnisUI ui) { if (q == null) { return; } try { - updateVariables(p); + updateVariables(p, corpusConfigs); if (p.getCorpusGraphs().isEmpty()) { p.createCorpusGraph(); } SCorpusGraph corpusGraph = p.getCorpusGraphs().get(0); - AbstractComponent newPanel = - createSingleResultPanel(corpusGraph, currentResults, q.getOffset(), allMatches); + AbstractComponent newPanel = createSingleResultPanel(corpusGraph, currentResults, + q.getOffset(), allMatches, corpusConfigs); currentResults += 1; int numberOfResults = allMatches.size(); String strResults = numberOfResults > 1 ? "results" : "result"; - sui.getSearchView().getControlPanel().getQueryPanel().setStatus( - sui.getSearchView().getControlPanel().getQueryPanel().getLastPublicStatus(), + ui.getSearchView().getControlPanel().getQueryPanel().setStatus( + ui.getSearchView().getControlPanel().getQueryPanel().getLastPublicStatus(), " (showing " + currentResults + "/" + numberOfResults + " " + strResults + ")"); resultPanelList.add(newPanel); resultLayout.addComponent(newPanel); if (newPanel instanceof SingleResultPanel) { ((SingleResultPanel) newPanel) - .setSegmentationLayer(sui.getQueryState().getVisibleBaseText().getValue()); + .setSegmentationLayer(ui.getQueryState().getVisibleBaseText().getValue()); } @@ -246,7 +242,7 @@ public void attach() { private AbstractComponent createSingleResultPanel(SCorpusGraph corpusGraph, int localMatchIndex, - long globalOffset, List allMatches) { + long globalOffset, List allMatches, Map corpusConfigs) { Match m = new Match(); if (allMatches != null && localMatchIndex >= 0 && localMatchIndex < allMatches.size()) { m = allMatches.get(localMatchIndex); @@ -258,9 +254,8 @@ private AbstractComponent createSingleResultPanel(SCorpusGraph corpusGraph, int .filter(d -> d.getDocumentGraph() != null && !d.getDocumentGraph().getNodes().isEmpty()) .findFirst(); if (doc.isPresent()) { - panel = new SingleResultPanel(doc.get(), m, localMatchIndex + globalOffset, - new ResolverProviderImpl(cacheResolver), sui, getVisibleTokenAnnos(), segmentationName, - controller, initialQuery); + panel = new SingleResultPanel(doc.get(), m, localMatchIndex + globalOffset, corpusConfigs, + getVisibleTokenAnnos(), segmentationName, controller, initialQuery); } else { Set matchedCorpora = new LinkedHashSet<>(); for (String id : m.getSaltIDs()) { @@ -344,16 +339,22 @@ private void setVisibleTokenAnnosVisible(SortedSet annos) { } public void showFinishedSubgraphSearch() { - // Search complete, stop progress bar control - if (sui.getSearchView().getControlPanel().getQueryPanel().getPiCount() != null) { - if (sui.getSearchView().getControlPanel().getQueryPanel().getPiCount().isVisible()) { - sui.getSearchView().getControlPanel().getQueryPanel().getPiCount().setVisible(false); - sui.getSearchView().getControlPanel().getQueryPanel().getPiCount().setEnabled(false); + UI uiRaw = UI.getCurrent(); + if (uiRaw instanceof AnnisUI) { + AnnisUI sui = (AnnisUI) uiRaw; + // Search complete, stop progress bar control + if (sui.getSearchView().getControlPanel().getQueryPanel().getPiCount() != null) { + if (sui.getSearchView().getControlPanel().getQueryPanel().getPiCount().isVisible()) { + sui.getSearchView().getControlPanel().getQueryPanel().getPiCount().setVisible(false); + sui.getSearchView().getControlPanel().getQueryPanel().getPiCount().setEnabled(false); + } } + // also remove the info how many results have been fetched + QueryPanel qp = sui.getSearchView().getControlPanel().getQueryPanel(); + + qp.setStatus(qp.getLastPublicStatus()); } - // also remove the info how many results have been fetched - QueryPanel qp = sui.getSearchView().getControlPanel().getQueryPanel(); - qp.setStatus(qp.getLastPublicStatus()); + } /** @@ -421,20 +422,24 @@ private void updateSegmentationLayer(Set segLayers) { * Check if a segmentation item must set checked. If no segmentation layer is selected, set * the default layer as selected. */ - final String selectedSegmentationLayer = - sui.getQueryState().getVisibleBaseText().getValue(); - if ((selectedSegmentationLayer == null && "".equals(s)) - - || s.equals(selectedSegmentationLayer)) { - miSingleSegLayer.setChecked(true); - } else { - miSingleSegLayer.setChecked(false); + UI ui = UI.getCurrent(); + if (ui instanceof AnnisUI) { + final String selectedSegmentationLayer = + ((AnnisUI) ui).getQueryState().getVisibleBaseText().getValue(); + if ((selectedSegmentationLayer == null && "".equals(s)) + + || s.equals(selectedSegmentationLayer)) { + miSingleSegLayer.setChecked(true); + } else { + miSingleSegLayer.setChecked(false); + } } } } // end iterate for segmentation layer } - private void updateVariables(SaltProject p) throws UnsupportedEncodingException { + private void updateVariables(SaltProject p, Map corpusConfigMap) + throws UnsupportedEncodingException { segmentationLayerSet.addAll(getSegmentationNames(p)); tokenAnnotationLevelSet.addAll(Helper.getTokenAnnotationLevelSet(p)); Set hiddenTokenAnnos = null; @@ -443,7 +448,7 @@ private void updateVariables(SaltProject p) throws UnsupportedEncodingException for (String corpusName : corpusNames) { - CorpusConfiguration corpusConfig = Helper.getCorpusConfig(corpusName, UI.getCurrent()); + CorpusConfiguration corpusConfig = corpusConfigMap.get(corpusName); if (corpusConfig != null && corpusConfig.getView() != null && corpusConfig.getView().getHiddenAnnos() != null) { diff --git a/src/main/java/org/corpus_tools/annis/gui/resultview/SingleCorpusResultPanel.java b/src/main/java/org/corpus_tools/annis/gui/resultview/SingleCorpusResultPanel.java index 2de18ac179..cbfabc48d7 100644 --- a/src/main/java/org/corpus_tools/annis/gui/resultview/SingleCorpusResultPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/resultview/SingleCorpusResultPanel.java @@ -27,9 +27,9 @@ import java.util.List; import java.util.Optional; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.MetaDataPanel; +import org.corpus_tools.annis.gui.components.MetaDataPanel; import org.corpus_tools.annis.gui.objects.DisplayedResultQuery; +import org.corpus_tools.annis.gui.util.Helper; public class SingleCorpusResultPanel extends CssLayout { private static final long serialVersionUID = 3L; diff --git a/src/main/java/org/corpus_tools/annis/gui/resultview/SingleResultPanel.java b/src/main/java/org/corpus_tools/annis/gui/resultview/SingleResultPanel.java index eed26e6598..1f3cb96ca7 100644 --- a/src/main/java/org/corpus_tools/annis/gui/resultview/SingleResultPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/resultview/SingleResultPanel.java @@ -34,8 +34,12 @@ import com.vaadin.v7.ui.AbstractSelect; import com.vaadin.v7.ui.ComboBox; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -46,18 +50,22 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; +import org.corpus_tools.annis.api.model.CorpusConfiguration; import org.corpus_tools.annis.api.model.VisualizerRule; +import org.corpus_tools.annis.api.model.VisualizerRule.ElementEnum; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.IDGenerator; -import org.corpus_tools.annis.gui.MetaDataPanel; -import org.corpus_tools.annis.gui.QueryController; +import org.corpus_tools.annis.gui.components.MetaDataPanel; +import org.corpus_tools.annis.gui.controller.QueryController; import org.corpus_tools.annis.gui.objects.DisplayedResultQuery; import org.corpus_tools.annis.gui.objects.Match; import org.corpus_tools.annis.gui.query_references.ShareSingleMatchGenerator; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.annis.gui.util.IDGenerator; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.common.SaltProject; +import org.corpus_tools.salt.core.SLayer; import org.corpus_tools.salt.core.SNode; +import org.corpus_tools.salt.core.SRelation; import org.slf4j.LoggerFactory; /** @@ -144,7 +152,6 @@ public void valueChange(Property.ValueChangeEvent event) { private SDocument result; - private final AnnisUI ui; private final List visualizers = new LinkedList<>(); private List resolverEntries; @@ -163,7 +170,7 @@ public void valueChange(Property.ValueChangeEvent event) { private final long resultNumber; - private final ResolverProvider resolverProvider; + private final Map corpusConfigs; private final Set visibleTokenAnnos; @@ -180,17 +187,16 @@ public void valueChange(Property.ValueChangeEvent event) { private final Match match; public SingleResultPanel(final SDocument result, Match match, long resultNumber, - ResolverProvider resolverProvider, AnnisUI ui, Set visibleTokenAnnos, + Map corpusConfigs, Set visibleTokenAnnos, String segmentationName, QueryController controller, DisplayedResultQuery query) { - this.ui = ui; this.result = result; this.segmentationName = segmentationName; this.queryController = controller; this.resultNumber = resultNumber; - this.resolverProvider = resolverProvider; this.visibleTokenAnnos = visibleTokenAnnos; this.query = query; this.match = match; + this.corpusConfigs = corpusConfigs; setWidth("100%"); setHeight("-1px"); @@ -333,7 +339,9 @@ public void attach() { super.attach(); initVisualizer(); - if (ui.getConfig().isShortenReferenceLinks() && !ui.isDesktopMode()) { + UI ui = UI.getCurrent(); + if (ui instanceof AnnisUI && ((AnnisUI) ui).getConfig().isShortenReferenceLinks() + && !((AnnisUI) ui).isDesktopMode()) { btLink.setVisible(true); } else { btLink.setVisible(false); @@ -370,8 +378,60 @@ public void changeContext(long resultNumber, int context, boolean left) { private void initVisualizer() { try { - resolverEntries = resolverProvider == null ? new LinkedList<>() - : resolverProvider.getResolverEntries(path.get(0), result, UI.getCurrent()); + String corpusName = path.get(0); + CorpusConfiguration configForThisResult = corpusConfigs.get(corpusName); + resolverEntries = new ArrayList<>(); + if (configForThisResult != null) { + // create a request for resolver entries + HashSet resolverRequests = new HashSet<>(); + + Set nodeLayers = new HashSet(); + Set edgeLayers = new HashSet(); + + if (result != null && result.getDocumentGraph() != null) { + for (SNode n : result.getDocumentGraph().getNodes()) { + for (SLayer layer : n.getLayers()) { + nodeLayers.add(layer.getName()); + } + } + + for (SRelation e : result.getDocumentGraph().getRelations()) { + for (SLayer layer : e.getLayers()) { + try { + edgeLayers.add(layer.getName()); + } catch (NullPointerException ex) { + log.warn("NullPointerException when using Salt, was trying to get layer name", ex); + } + } + } + + } + + for (String ns : nodeLayers) { + resolverRequests.add(new SingleResolverRequest(corpusName, ns, ElementEnum.NODE)); + } + for (String ns : edgeLayers) { + resolverRequests.add(new SingleResolverRequest(corpusName, ns, ElementEnum.EDGE)); + } + // Find all visualizers that match this result and make sure the matching rules are unique + LinkedHashSet matchingRules = new LinkedHashSet<>(); + for (VisualizerRule visRule : configForThisResult.getVisualizers()) { + for (SingleResolverRequest r : resolverRequests) { + if (visRule.getMappings() == null) { + visRule.setMappings(new LinkedHashMap<>()); + } + if (visRule.getElement() != null && !visRule.getElement().equals(r.getType())) { + continue; + } + if (visRule.getLayer() != null && !visRule.getLayer().equals(r.getNamespace())) { + continue; + } + matchingRules.add(visRule); + } + } + resolverEntries.addAll(matchingRules); + } + visualizers.clear(); List openVisualizers = new LinkedList<>(); @@ -388,7 +448,7 @@ private void initVisualizer() { String htmlID = "resolver-" + resultNumber + "_" + i; VisualizerPanel p = new VisualizerPanel(visRule, i, result, match, visibleTokenAnnos, - markedAndCovered, htmlID, resultID, this, segmentationName, ui); + markedAndCovered, htmlID, resultID, this, segmentationName); visualizers.add(p); @@ -463,7 +523,9 @@ private void showReloadingProgress() { private void showShareSingleMatchGenerator() { // select the current match - if (ui != null) { + UI rawUi = UI.getCurrent(); + if (rawUi instanceof AnnisUI) { + AnnisUI ui = (AnnisUI) rawUi; ui.getQueryState().getSelectedMatches().getValue().clear(); ui.getQueryState().getSelectedMatches().getValue().add(resultNumber); ui.getSearchView().updateFragment(ui.getQueryController().getSearchQuery()); @@ -478,7 +540,7 @@ private void showShareSingleMatchGenerator() { window.addCloseListener(e -> btLink.setEnabled(true)); window.setCaption("Match reference link"); - ui.addWindow(window); + rawUi.addWindow(window); } } diff --git a/src/main/java/org/corpus_tools/annis/gui/resultview/VisualizerPanel.java b/src/main/java/org/corpus_tools/annis/gui/resultview/VisualizerPanel.java index 111731504c..5afca953c8 100644 --- a/src/main/java/org/corpus_tools/annis/gui/resultview/VisualizerPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/resultview/VisualizerPanel.java @@ -41,15 +41,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.xml.stream.XMLStreamException; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.api.model.QueryLanguage; import org.corpus_tools.annis.api.model.VisualizerRule; import org.corpus_tools.annis.api.model.VisualizerRule.VisibilityEnum; import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.Background; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.CommonUI; import org.corpus_tools.annis.gui.components.ExceptionDialog; import org.corpus_tools.annis.gui.graphml.DocumentGraphMapper; import org.corpus_tools.annis.gui.media.MediaController; @@ -57,6 +54,7 @@ import org.corpus_tools.annis.gui.media.PDFViewer; import org.corpus_tools.annis.gui.objects.Match; import org.corpus_tools.annis.gui.objects.RawTextWrapper; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.FilteringVisualizerPlugin; import org.corpus_tools.annis.gui.visualizers.LoadableVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; @@ -70,6 +68,12 @@ import org.eclipse.emf.common.util.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Flux; /** * Controls the visibility of visualizer plugins and provides some control methods for the media @@ -81,15 +85,17 @@ */ public class VisualizerPanel extends CssLayout - implements Button.ClickListener, VisualizationToggle { + implements Button.ClickListener { private class BackgroundJob implements Runnable { private final Future future; private final LoadableVisualizer.Callback callback; + private UI ui; - public BackgroundJob(Future future, LoadableVisualizer.Callback callback) { + public BackgroundJob(Future future, LoadableVisualizer.Callback callback, UI ui) { this.future = future; this.callback = callback; + this.ui = ui; } @Override @@ -203,8 +209,6 @@ private void updateGUIAfterLoadingVisualizer(LoadableVisualizer.Callback callbac private ProgressBar progress; - private AnnisUI ui; - private VisualizerContextChanger visCtxChanger; private static final String UNKNOWN = ""; @@ -216,8 +220,7 @@ private void updateGUIAfterLoadingVisualizer(LoadableVisualizer.Callback callbac */ public VisualizerPanel(final VisualizerRule visRule, int visId, SDocument result, Match match, Set visibleTokenAnnos, Map markedAndCovered, String htmlID, - String resultID, VisualizerContextChanger parent, String segmentationName, AnnisUI ui) { - this.ui = ui; + String resultID, VisualizerContextChanger parent, String segmentationName) { this.visRule = visRule; this.visId = visId; @@ -251,7 +254,10 @@ public VisualizerPanel(final VisualizerRule visRule, int visId, SDocument result public void attach() { super.attach(); - if (visRule != null) { + UI uiRaw = UI.getCurrent(); + + if (uiRaw instanceof AnnisUI && visRule != null) { + AnnisUI ui = (AnnisUI) uiRaw; visPlugin = ui.getVisualizerPlugins().stream() .filter(plugin -> Objects.equal(plugin.getShortName(), visRule.getVisType())).findAny() .orElse(null); @@ -289,7 +295,7 @@ public void attach() { // create the visualizer and calc input try { - vis = createComponent(); + vis = createComponent(ui); if (vis != null) { vis.setVisible(true); addComponent(vis); @@ -326,12 +332,12 @@ public void buttonClick(ClickEvent event) { toggleVisualizer(isVisible, null); } - private Component createComponent() { + private Component createComponent(CommonUI ui) { if (visPlugin == null) { return null; } - final VisualizerInput input = createInput(); + final VisualizerInput input = createInput(ui); Component c = visPlugin.createComponent(input, this); if (c == null) { @@ -344,7 +350,7 @@ private Component createComponent() { return c; } - private VisualizerInput createInput() { + private VisualizerInput createInput(CommonUI ui) { VisualizerInput input = new VisualizerInput(); input.setUI(ui); input.setContextPath(ui.getServletContext().getContextPath()); @@ -394,25 +400,29 @@ private VisualizerInput createInput() { } - private SaltProject getDocument(List nodeAnnoFilter, boolean useRawText, UI ui) { + private SaltProject getDocument(List nodeAnnoFilter, boolean useRawText, CommonUI ui) { try { - CorporaApi api = new CorporaApi(Helper.getClient(ui)); + WebClient client = ui.getWebClient(); // Reconstruct the document node name from the raw path of the match String documentNodeName = Joiner.on('/').join(documentPathRaw); String aql = Helper.buildDocumentQuery(documentNodeName, nodeAnnoFilter, useRawText); - File graphML = api.subgraphForQuery(documentPathDecoded.get(0), aql, QueryLanguage.AQL, null); try { final SaltProject p = SaltFactory.createSaltProject(); SCorpusGraph cg = p.createCorpusGraph(); URI docURI = URI.createURI("salt:/" + Joiner.on('/').join(documentPathRaw)); SDocument doc = cg.createDocument(docURI); + + File graphML = File.createTempFile("annis-subgraph", ".salt"); + Flux response = client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/subgraph-for-query") + .queryParam("query", aql).queryParam("query_language", QueryLanguage.AQL) + .build(documentPathDecoded.get(0))) + .accept(MediaType.APPLICATION_XML).retrieve().bodyToFlux(DataBuffer.class); + DataBufferUtils.write(response, graphML.toPath()).block(); SDocumentGraph docGraph = DocumentGraphMapper.map(graphML); - if (Files.deleteIfExists(graphML.toPath())) { - log.debug("Could not delete temporary SaltXML file {} because it does not exist.", - graphML.getPath()); - } + Files.deleteIfExists(graphML.toPath()); doc.setDocumentGraph(docGraph); return p; @@ -420,7 +430,7 @@ private SaltProject getDocument(List nodeAnnoFilter, boolean useRawText, log.error("Could not map GraphML to Salt", ex); ui.access(() -> ExceptionDialog.show(ex, "Could not map GraphML to Salt", ui)); } - } catch (ApiException e) { + } catch (WebClientResponseException e) { log.error("General remote service exception", e); } return null; @@ -441,7 +451,9 @@ private String getVisualizerShortNameDebug() { private void loadVisualizer(final LoadableVisualizer.Callback callback) { - if (visPlugin != null) { + UI uiRaw = UI.getCurrent(); + if (uiRaw instanceof CommonUI && visPlugin != null) { + CommonUI ui = (CommonUI) uiRaw; btEntry.setIcon(ICON_COLLAPSE); progress.setIndeterminate(true); progress.setVisible(true); @@ -453,14 +465,14 @@ private void loadVisualizer(final LoadableVisualizer.Callback callback) { final Future future = execService.submit(() -> { // only create component if not already created if (vis == null) { - return createComponent(); + return createComponent(ui); } else { return vis; } }); // run the actual code to load the visualizer - Background.run(new BackgroundJob(future, callback)); + Background.run(new BackgroundJob(future, callback, ui)); } // end if create input was needed @@ -482,7 +494,6 @@ public void setVisibleTokenAnnosVisible(SortedSet annos) { } } - @Override public void toggleVisualizer(boolean visible, LoadableVisualizer.Callback callback) { if (visible) { loadVisualizer(callback); @@ -500,7 +511,6 @@ public void toggleVisualizer(boolean visible, LoadableVisualizer.Callback callba } - @Override public boolean visualizerIsVisible() { return vis != null && vis.isVisible(); } diff --git a/src/main/java/org/corpus_tools/annis/gui/security/AuthenticationSuccessListener.java b/src/main/java/org/corpus_tools/annis/gui/security/AuthenticationSuccessListener.java deleted file mode 100644 index 28095aa714..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/security/AuthenticationSuccessListener.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.corpus_tools.annis.gui.security; - -import java.io.Serializable; -import org.springframework.context.ApplicationListener; -import org.springframework.security.authentication.event.AuthenticationSuccessEvent; -import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken; -import org.springframework.stereotype.Component; - -@Component -public class AuthenticationSuccessListener implements - ApplicationListener, Serializable { - - private static final long serialVersionUID = 108867628342416834L; - private String token; - - @Override - public void onApplicationEvent(AuthenticationSuccessEvent event) { - if(event.getAuthentication() instanceof OAuth2LoginAuthenticationToken) { - OAuth2LoginAuthenticationToken auth = (OAuth2LoginAuthenticationToken) event.getAuthentication(); - this.token = auth.getAccessToken().getTokenValue(); - } - } - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } -} diff --git a/src/main/java/org/corpus_tools/annis/gui/security/AutoTokenRefreshClient.java b/src/main/java/org/corpus_tools/annis/gui/security/AutoTokenRefreshClient.java deleted file mode 100644 index fd641bd38e..0000000000 --- a/src/main/java/org/corpus_tools/annis/gui/security/AutoTokenRefreshClient.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.corpus_tools.annis.gui.security; - -import java.lang.reflect.Type; -import java.util.Optional; -import okhttp3.Call; -import org.corpus_tools.annis.ApiCallback; -import org.corpus_tools.annis.ApiClient; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.ApiResponse; -import org.corpus_tools.annis.gui.CommonUI; -import org.corpus_tools.annis.gui.Helper; -import org.springframework.security.oauth2.core.user.OAuth2User; - -public class AutoTokenRefreshClient extends ApiClient { - - private final AuthenticationSuccessListener authListener; - private final CommonUI ui; - - public AutoTokenRefreshClient(CommonUI ui, AuthenticationSuccessListener authListener) { - super(); - this.ui = ui; - this.authListener = authListener; - - setReadTimeout(0); - - // Use the configuration to allow changing the path to the web-service - setBasePath(ui.getConfig().getWebserviceUrl()); - - - updateToken(); - } - - private void updateToken() { - final Optional user = Helper.getUser(ui.getSecurityContext()); - if (user.isPresent()) { - setBearerToken(authListener.getToken()); - } else { - setBearerToken(null); - } - } - - @Override - public ApiResponse execute(Call call, Type returnType) throws ApiException { - updateToken(); - return super.execute(call, returnType); - } - - @Override - public void executeAsync(Call call, Type returnType, ApiCallback callback) { - updateToken(); - super.executeAsync(call, returnType, callback); - } -} diff --git a/src/main/java/org/corpus_tools/annis/gui/LoginListener.java b/src/main/java/org/corpus_tools/annis/gui/security/LoginListener.java similarity index 95% rename from src/main/java/org/corpus_tools/annis/gui/LoginListener.java rename to src/main/java/org/corpus_tools/annis/gui/security/LoginListener.java index ca2d1fc8ef..d03a041e7d 100644 --- a/src/main/java/org/corpus_tools/annis/gui/LoginListener.java +++ b/src/main/java/org/corpus_tools/annis/gui/security/LoginListener.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.security; import java.io.Serializable; diff --git a/src/main/java/org/corpus_tools/annis/gui/security/SecurityConfiguration.java b/src/main/java/org/corpus_tools/annis/gui/security/SecurityConfiguration.java index 94e959b6fa..3efe7f39f1 100644 --- a/src/main/java/org/corpus_tools/annis/gui/security/SecurityConfiguration.java +++ b/src/main/java/org/corpus_tools/annis/gui/security/SecurityConfiguration.java @@ -1,15 +1,36 @@ package org.corpus_tools.annis.gui.security; +import com.fasterxml.jackson.dataformat.toml.TomlMapper; +import com.vaadin.spring.annotation.EnableVaadin; +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.Optional; +import org.corpus_tools.annis.gui.ServiceStarter; +import org.corpus_tools.annis.gui.UIConfig; +import org.corpus_tools.annis.gui.controller.CorpusGraphMessageConverter; +import org.corpus_tools.annis.gui.controller.DocumentGraphMessageConverter; import org.springframework.boot.autoconfigure.condition.NoneNestedConditions; import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConfiguredCondition; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - - +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; +import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.reactive.function.client.WebClient; + +@EnableVaadin @Configuration public class SecurityConfiguration { @@ -20,7 +41,6 @@ public class SecurityConfiguration { public static final String FRAGMENT_TO_RESTORE = "ANNIS_FRAGENT_TO_RESTORE"; - private static class NoClientsConfiguredCondition extends NoneNestedConditions { NoClientsConfiguredCondition() { super(ConfigurationPhase.REGISTER_BEAN); @@ -33,41 +53,101 @@ static class ClientsConfigured { // NO_UCD (unused code) @EnableWebSecurity @Conditional(ClientsConfiguredCondition.class) - public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { // NO_UCD - // (unused - // code) + public static class OAuth2LoginSecurityConfig { - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { configureHttpVaadinSecurity(http); // Configure logout http.logout().logoutUrl(LOGOUT_URL).logoutSuccessUrl(LOGOUT_SUCCESS_URL) // Configure OAuth2 for login .and().oauth2Login(); + return http.build(); } - - @Override - public void configure(WebSecurity web) { - ignoreVaadinWebSecurity(web); + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return SecurityConfiguration::ignoreVaadinWebSecurity; } - } @EnableWebSecurity @Conditional(NoClientsConfiguredCondition.class) - public static class NoLoginSecurityConfig extends WebSecurityConfigurerAdapter { // NO_UCD (unused - // code) + public static class NoLoginSecurityConfig { - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { configureHttpVaadinSecurity(http); + return http.build(); + } + + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return SecurityConfiguration::ignoreVaadinWebSecurity; + } + } + + @Bean + WebClient webClient(UIConfig config, ServiceStarter serviceStarter, + Optional clientRegistrationRepository, + Optional authorizedClientRepository) throws IOException { + + // Use the provided service configuration to get the correct port of the + // graphANNIS service + File serviceConfigFile = serviceStarter.getServiceConfig(); + TomlMapper mapper = new TomlMapper(); + Map parsedServiceConfig = mapper.readValue(serviceConfigFile, Map.class); + String serviceURL = getServiceURL(parsedServiceConfig); + + WebClient.Builder builder = WebClient.builder().baseUrl(serviceURL).codecs(codecsConfigure -> { + codecsConfigure.customCodecs().register(new DocumentGraphMessageConverter()); + codecsConfigure.customCodecs().register(new CorpusGraphMessageConverter()); + }); + Optional desktopUserToken = serviceStarter.getDesktopUserToken(); + if (desktopUserToken.isPresent()) { + // Use the static provided token to authenticate against the REST service + builder = builder.defaultHeader("Authorization", + "Bearer " + desktopUserToken.get().getCredentials().toString()); + } else if (clientRegistrationRepository.isPresent() && authorizedClientRepository.isPresent()) { + // Use the token that can be acquired by logging in + ServletOAuth2AuthorizedClientExchangeFilterFunction filter = + new ServletOAuth2AuthorizedClientExchangeFilterFunction( + clientRegistrationRepository.get(), authorizedClientRepository.get()); + + filter.setDefaultOAuth2AuthorizedClient(true); + builder = builder.filter(filter); } - @Override - public void configure(WebSecurity web) { - ignoreVaadinWebSecurity(web); + return builder.build(); + } + + private static String getServiceURL(Map serviceConfig) { + long port = 5711l; + Object bindSection = serviceConfig.get("bind"); + if (bindSection instanceof Map) { + @SuppressWarnings("rawtypes") + Object portRaw = ((Map) bindSection).get("port"); + if (portRaw instanceof Long) { + port = (Long) portRaw; + } } + return "http://localhost:" + port + "/v1"; + } + + @Bean + @Conditional(ClientsConfiguredCondition.class) + public OAuth2AuthorizedClientManager authorizedClientManager( + ClientRegistrationRepository clientRegistrationRepository, + OAuth2AuthorizedClientRepository authorizedClientRepository) { + + OAuth2AuthorizedClientProvider authorizedClientProvider = + OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build(); + + DefaultOAuth2AuthorizedClientManager authorizedClientManager = + new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, + authorizedClientRepository); + authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); + return authorizedClientManager; } private static void ignoreVaadinWebSecurity(WebSecurity web) { @@ -97,9 +177,11 @@ private static void configureHttpVaadinSecurity(HttpSecurity http) throws Except // Not using Spring CSRF protection here because Vaadin also has a // Cross-site request forgery protection running. - // Spring will try to enforce an additional layer on the filtered resources, which conflicts + // Spring will try to enforce an additional layer on the filtered resources, + // which conflicts // with the Vaadin CSRF protection and makes the frontend unusable. - // Disabling Spring CSRF is therefore safe, as long as Vaadin CSRF protection is activated + // Disabling Spring CSRF is therefore safe, as long as Vaadin CSRF protection is + // activated // (which it is per default). // Also see // https://vaadin.com/blog/filter-based-spring-security-in-vaadin-applications diff --git a/src/main/java/org/corpus_tools/annis/gui/Helper.java b/src/main/java/org/corpus_tools/annis/gui/util/Helper.java similarity index 87% rename from src/main/java/org/corpus_tools/annis/gui/Helper.java rename to src/main/java/org/corpus_tools/annis/gui/util/Helper.java index 568ebd39f5..65327f35de 100644 --- a/src/main/java/org/corpus_tools/annis/gui/Helper.java +++ b/src/main/java/org/corpus_tools/annis/gui/util/Helper.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.util; import static org.corpus_tools.annis.gui.objects.AnnisConstants.ANNIS_NS; import static org.corpus_tools.annis.gui.objects.AnnisConstants.FEAT_MATCHEDNODE; @@ -55,25 +55,21 @@ import java.util.TreeSet; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.xml.stream.XMLStreamException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.MultiPartEmail; -import org.corpus_tools.annis.ApiClient; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.api.SearchApi; import org.corpus_tools.annis.api.model.AnnoKey; +import org.corpus_tools.annis.api.model.Annotation; import org.corpus_tools.annis.api.model.AnnotationComponentType; import org.corpus_tools.annis.api.model.Component; import org.corpus_tools.annis.api.model.CorpusConfiguration; -import org.corpus_tools.annis.api.model.CorpusConfigurationContext; -import org.corpus_tools.annis.api.model.CorpusConfigurationView; import org.corpus_tools.annis.api.model.FindQuery; import org.corpus_tools.annis.api.model.FindQuery.OrderEnum; import org.corpus_tools.annis.api.model.QueryLanguage; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.UIConfig; import org.corpus_tools.annis.gui.components.ExceptionDialog; import org.corpus_tools.annis.gui.graphml.CorpusGraphMapper; import org.corpus_tools.annis.gui.objects.AnnisConstants; @@ -106,12 +102,21 @@ import org.corpus_tools.salt.graph.Label; import org.corpus_tools.salt.util.DataSourceSequence; import org.slf4j.LoggerFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.MediaType; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.core.oidc.StandardClaimNames; import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.function.Tuple2; +import reactor.util.function.Tuples; /** * @@ -372,8 +377,6 @@ public void nodeReached(final GRAPH_TRAVERSE_TYPE traversalType, final String tr } } - public final static String DEFAULT_CONFIG = "default-config"; - // the name of the web font class, the css class contains !important. public final static String CORPUS_FONT_FORCE = "corpus-font-force"; @@ -477,71 +480,40 @@ public static String convertExceptionToMessage(final Throwable ex) { return sb.toString(); } - public static ApiClient getClient(final UI ui) { - if (ui instanceof CommonUI) { - CommonUI annisUI = (CommonUI) ui; - return annisUI.getClient(); - } - return null; - } - - public static Set getMetaAnnotationNames(final String corpus, final UI ui) - throws ApiException { - final CorporaApi api = new CorporaApi(getClient(ui)); - final SearchApi search = new SearchApi(getClient(ui)); - - final List nodeAnnos = - api.nodeAnnotations(corpus, false, true).stream() - .filter(a -> !Objects.equals(a.getKey().getNs(), "annis") - && !Objects.equals(a.getKey().getName(), "tok")) - .collect(Collectors.toList()); + public static Flux getMetaAnnotationNames(final String corpus, WebClient client) { - final Set metaAnnos = new HashSet<>(); + Flux nodeAnnos = client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/node-annotations") + .queryParam("list_values", false).queryParam("only_most_frequent_values", true) + .build(corpus)) + .retrieve().bodyToFlux(Annotation.class) + .filter(a -> !Objects.equals(a.getKey().getNs(), "annis") + && !Objects.equals(a.getKey().getName(), "tok")); // Check for each annotation if its actually a meta-annotation - for (final org.corpus_tools.annis.api.model.Annotation a : nodeAnnos) { - - String annotationName = getQName(a.getKey()); - if (annotationIsMetadata(corpus, annotationName, search)) { - metaAnnos.add(a.getKey()); - } - } - - return metaAnnos; - } - - private static boolean annotationIsMetadata(String corpus, String annotationName, - SearchApi search) throws ApiException { - if (!validQNamePattern.matcher(annotationName).matches()) { - return false; - } - - final FindQuery q = new FindQuery(); - q.setCorpora(Arrays.asList(corpus)); - - q.setQuery("annis:node_type=\"corpus\" _ident_ " + annotationName); - // Not sorting the results is much faster, especially if we only fetch the first - // item (we are only interested if a match exists, not how many items or which one) - q.setOrder(OrderEnum.NOTSORTED); - q.setLimit(1); - q.setOffset(0); + Flux metaAnnos = nodeAnnos.map(a -> { + return Tuples.of(a, getQName(a.getKey())); + }).filter(t -> validQNamePattern.matcher(t.getT2()).matches()).flatMap(t -> { + final FindQuery q = new FindQuery(); + q.setCorpora(Arrays.asList(corpus)); + + q.setQuery("annis:node_type=\"corpus\" _ident_ " + t.getT2()); + // Not sorting the results is much faster, especially if we only fetch the first + // item (we are only interested if a match exists, not how many items or which one) + q.setOrder(OrderEnum.NOTSORTED); + q.setLimit(1); + q.setOffset(0); + q.setQueryLanguage(QueryLanguage.AQL); + + // Fetch first item, if there is no match the result will be empty + Flux findResult = + client.post().uri("/search/find").bodyValue(q).retrieve().bodyToFlux(String.class); + return findResult.zipWith(Mono.just(t.getT1()).repeat()); + }).filter(t -> t.getT1() != null && !t.getT1().trim().isEmpty()) + .map(Tuple2::getT2); + return metaAnnos.map(a -> a.getKey()).distinct(); - q.setQueryLanguage(QueryLanguage.AQL); - final File findResult = search.find(q); - if (findResult != null && findResult.isFile()) - try { - try (Stream lines = Files.lines(findResult.toPath(), StandardCharsets.UTF_8)) { - Optional anyLine = lines.findAny(); - if (anyLine.isPresent() && !anyLine.get().isEmpty()) { - return true; - } - } - } catch (final IOException ex) { - log.error("Error when accessing file with find results", ex); - } - return false; } - public static String getQName(final AnnoKey key) { if (key == null) { return null; @@ -627,47 +599,40 @@ public static String generateCorpusLink(final Set corpora) { * Loads the corpus config of a specific corpus. * * @param corpus The name of the corpus, for which the config is fetched. - * @return A {@link CorpusConfig} object, which wraps a {@link Properties} object. This Properties - * object stores the corpus configuration as simple key-value pairs. + * @return A {@link Mono} for a {@link CorpusConfig} object, which wraps a {@link Properties} + * object. This Properties object stores the corpus configuration as simple key-value + * pairs. */ - public static CorpusConfiguration getCorpusConfig(final String corpus, final UI ui) { + public static Mono getCorpusConfig(final String corpus, final UI ui) { if (corpus == null || corpus.isEmpty()) { Notification.show("no corpus is selected", "please select at least one corpus and execute query again", Notification.Type.WARNING_MESSAGE); - return null; - } + return Mono.empty(); + } else if (ui instanceof CommonUI) { + CommonUI commonUI = (CommonUI) ui; - CorpusConfiguration corpusConfig = new CorpusConfiguration(); + WebClient client = commonUI.getWebClient(); - final CorporaApi api = new CorporaApi(getClient(ui)); + return client.get().uri("/corpora/{corpus}/configuration", corpus).retrieve() + .bodyToMono(CorpusConfiguration.class); - try { - corpusConfig = api.corpusConfiguration(corpus); - } catch (final ApiException ex) { - ui.access(() -> ExceptionDialog.show(ex, ERROR_MESSAGE_CORPUS_PROPS_HEADER, ui)); + } else { + return Mono.empty(); } - - return corpusConfig; } - - public static CorpusConfiguration getDefaultCorpusConfig() { - - final CorpusConfiguration defaultCorpusConfig = new CorpusConfiguration(); - - defaultCorpusConfig.setView(new CorpusConfigurationView()); - defaultCorpusConfig.setContext(new CorpusConfigurationContext()); - defaultCorpusConfig.setExampleQueries(new LinkedList<>()); - defaultCorpusConfig.setVisualizers(new LinkedList<>()); - - defaultCorpusConfig.getView().setPageSize(10); - defaultCorpusConfig.getContext().setDefault(5); - defaultCorpusConfig.getContext().setSizes(Arrays.asList(1, 2, 5, 10)); - defaultCorpusConfig.getContext().setMax(Integer.MAX_VALUE); - - return defaultCorpusConfig; + public static Mono> getCorpusConfigurationMap( + Collection corpora, WebClient client) { + Mono> corpusConfigs = + Flux.fromIterable(corpora).flatMap(corpus -> { + Mono result = + client.get().uri("/corpora/{corpus}/configuration", corpus).retrieve() + .bodyToMono(CorpusConfiguration.class); + return result.zipWith(Mono.just(corpus)); + }).collectMap(Tuple2::getT2, Tuple2::getT1); + return corpusConfigs; } @@ -698,13 +663,12 @@ public static Range getLeftRightSpan(final SNode node, final SDocumentG * be fetched. * @return Returns a corpus graph that contains the meta data as (sub-) corpus annotation. */ - public static SCorpusGraph getMetaData(final String toplevelCorpusName, + public static Mono getMetaData(final String toplevelCorpusName, final Optional documentName, final UI ui) { - final SearchApi api = new SearchApi(Helper.getClient(ui)); - - try { - // Get the corpus graph and with it the meta data on the corpus/document nodes + if (ui instanceof CommonUI) { + CommonUI commonUI = (CommonUI) ui; + WebClient client = commonUI.getWebClient(); String aql; if (documentName.isPresent()) { aql = "(annis:node_type=\"corpus\" _ident_ annis:doc=/" @@ -716,20 +680,15 @@ public static SCorpusGraph getMetaData(final String toplevelCorpusName, aql = "annis:node_type=\"corpus\" _ident_ annis:node_name=/" + AQL_REGEX_VALUE_ESCAPER.escape(toplevelCorpusName) + "/"; } - final File graphML = api.subgraphForQuery(toplevelCorpusName, aql, QueryLanguage.AQL, - AnnotationComponentType.PARTOF); - SCorpusGraph result = CorpusGraphMapper.map(graphML); - if (Files.deleteIfExists(graphML.toPath())) { - log.debug("Could not delete temporary SaltXML file {} because it does not exist.", - graphML.getPath()); - } - return result; - } catch (ApiException | XMLStreamException | IOException ex) { - log.error(null, ex); - ui.access(() -> ExceptionDialog.show(ex, "Could not retrieve metadata", ui)); - } - return SaltFactory.createSCorpusGraph(); + return client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/subgraph-for-query") + .queryParam("query", aql).queryParam("query_language", QueryLanguage.AQL) + .queryParam("component_type_filter", AnnotationComponentType.PARTOF) + .build(toplevelCorpusName)) + .accept(MediaType.APPLICATION_XML).retrieve().bodyToMono(SCorpusGraph.class); + } + return Mono.empty(); } /** @@ -742,30 +701,34 @@ public static SCorpusGraph getMetaData(final String toplevelCorpusName, public static List getMetaDataDoc(final String toplevelCorpusName, final String documentName, final UI ui) { final List result = new ArrayList<>(); - final SearchApi api = new SearchApi(Helper.getClient(ui)); - - try { + if (ui instanceof CommonUI) { + CommonUI commonUI = (CommonUI) ui; + WebClient client = commonUI.getWebClient(); - // Get the corpus graph and with it the meta data on the corpus/document nodes - final File graphML = api.subgraphForQuery(toplevelCorpusName, - "annis:node_type=\"corpus\" _ident_ annis:doc=/" - + AQL_REGEX_VALUE_ESCAPER.escape(documentName) + "/", - QueryLanguage.AQL, AnnotationComponentType.PARTOF); + try { - final SCorpusGraph cg = CorpusGraphMapper.map(graphML); - if (Files.deleteIfExists(graphML.toPath())) { - log.debug("Could not delete temporary SaltXML file {} because it does not exist.", - graphML.getPath()); - } + // Get the corpus graph and with it the meta data on the corpus/document nodes + File graphML = File.createTempFile("annis-subgraph", ".salt"); + Flux response = client.get() + .uri(uriBuilder -> uriBuilder.path("/corpora/{corpus}/subgraph-for-query") + .queryParam("query", + "annis:node_type=\"corpus\" _ident_ annis:doc=/" + + AQL_REGEX_VALUE_ESCAPER.escape(documentName) + "/") + .queryParam("query_language", QueryLanguage.AQL) + .queryParam("component_type_filter", AnnotationComponentType.PARTOF) + .build(toplevelCorpusName)) + .accept(MediaType.APPLICATION_XML).retrieve().bodyToFlux(DataBuffer.class); + DataBufferUtils.write(response, graphML.toPath()).block(); + final SCorpusGraph cg = CorpusGraphMapper.map(graphML); + Files.deleteIfExists(graphML.toPath()); + for (final SNode n : cg.getNodes()) { + result.addAll(n.getMetaAnnotations()); + } - for (final SNode n : cg.getNodes()) { - result.addAll(n.getMetaAnnotations()); + } catch (WebClientResponseException | XMLStreamException | IOException ex) { + ui.access(() -> ExceptionDialog.show(ex, "Could not retrieve metadata for document", ui)); } - - } catch (ApiException | XMLStreamException | IOException ex) { - ui.access(() -> ExceptionDialog.show(ex, "Could not retrieve metadata for document", ui)); } - return result; } @@ -786,12 +749,8 @@ public static String getQualifiedName(final SAnnotation anno) { return ""; } - public static Optional getUser(UI ui) { - if (ui instanceof AnnisUI) { - return Helper.getUser(((AnnisUI) ui).getSecurityContext()); - } else { - return Helper.getUser(SecurityContextHolder.getContext()); - } + public static Optional getUser() { + return Helper.getUser(SecurityContextHolder.getContext()); } public static Optional getUser(SecurityContext context) { diff --git a/src/main/java/org/corpus_tools/annis/gui/IDGenerator.java b/src/main/java/org/corpus_tools/annis/gui/util/IDGenerator.java similarity index 99% rename from src/main/java/org/corpus_tools/annis/gui/IDGenerator.java rename to src/main/java/org/corpus_tools/annis/gui/util/IDGenerator.java index 584d4119b2..aa383090b2 100644 --- a/src/main/java/org/corpus_tools/annis/gui/IDGenerator.java +++ b/src/main/java/org/corpus_tools/annis/gui/util/IDGenerator.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.util; import com.google.common.base.Preconditions; import com.vaadin.ui.Component; diff --git a/src/main/java/org/corpus_tools/annis/gui/PDFPageHelper.java b/src/main/java/org/corpus_tools/annis/gui/util/PDFPageHelper.java similarity index 99% rename from src/main/java/org/corpus_tools/annis/gui/PDFPageHelper.java rename to src/main/java/org/corpus_tools/annis/gui/util/PDFPageHelper.java index 77de1df6c6..f12be7350f 100644 --- a/src/main/java/org/corpus_tools/annis/gui/PDFPageHelper.java +++ b/src/main/java/org/corpus_tools/annis/gui/util/PDFPageHelper.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.util; import java.util.List; import java.util.Map; diff --git a/src/main/java/org/corpus_tools/annis/gui/VersionInfo.java b/src/main/java/org/corpus_tools/annis/gui/util/VersionInfo.java similarity index 98% rename from src/main/java/org/corpus_tools/annis/gui/VersionInfo.java rename to src/main/java/org/corpus_tools/annis/gui/util/VersionInfo.java index 21bbbcf00e..80f8d7e4ba 100644 --- a/src/main/java/org/corpus_tools/annis/gui/VersionInfo.java +++ b/src/main/java/org/corpus_tools/annis/gui/util/VersionInfo.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.corpus_tools.annis.gui; +package org.corpus_tools.annis.gui.util; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/AbstractVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/AbstractVisualizer.java index 4f6581d8c6..deeb19485f 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/AbstractVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/AbstractVisualizer.java @@ -14,10 +14,10 @@ package org.corpus_tools.annis.gui.visualizers; import com.vaadin.ui.Component; -import com.vaadin.ui.UI; import java.util.List; import java.util.Map; import java.util.Set; +import org.corpus_tools.annis.gui.CommonUI; import org.corpus_tools.salt.core.SNode; /** @@ -36,7 +36,7 @@ public abstract class AbstractVisualizer implements VisualizerPlugin, FilteringV @Override public List getFilteredNodeAnnotationNames(String toplevelCorpusName, - String toplevelCorpusId, Map mappings, UI ui) { + String toplevelCorpusId, Map mappings, CommonUI ui) { return null; } diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/FilteringVisualizerPlugin.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/FilteringVisualizerPlugin.java index de85a9d168..92f0dff21a 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/FilteringVisualizerPlugin.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/FilteringVisualizerPlugin.java @@ -13,9 +13,9 @@ */ package org.corpus_tools.annis.gui.visualizers; -import com.vaadin.ui.UI; import java.util.List; import java.util.Map; +import org.corpus_tools.annis.gui.CommonUI; import org.corpus_tools.salt.common.SCorpus; /** @@ -36,5 +36,5 @@ public interface FilteringVisualizerPlugin { * @see SCorpus#getId() */ public List getFilteredNodeAnnotationNames(String toplevelCorpusName, - String toplevelCorpusId, Map mappings, UI ui); + String toplevelCorpusId, Map mappings, CommonUI ui); } diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/VisualizerInput.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/VisualizerInput.java index a9aaf35048..463f2e3ee1 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/VisualizerInput.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/VisualizerInput.java @@ -19,8 +19,8 @@ import java.util.Map; import java.util.Set; import org.corpus_tools.annis.gui.CommonUI; -import org.corpus_tools.annis.gui.FontConfig; -import org.corpus_tools.annis.gui.MatchedNodeColors; +import org.corpus_tools.annis.gui.objects.FontConfig; +import org.corpus_tools.annis.gui.objects.MatchedNodeColors; import org.corpus_tools.annis.gui.objects.RawTextWrapper; import org.corpus_tools.salt.SaltFactory; import org.corpus_tools.salt.common.SDocument; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/VisualizerPlugin.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/VisualizerPlugin.java index f057d86fde..cd801fe015 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/VisualizerPlugin.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/VisualizerPlugin.java @@ -17,7 +17,7 @@ import java.io.Serializable; import java.util.Map; import java.util.Set; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.salt.core.SNode; /** @@ -50,7 +50,7 @@ public interface VisualizerPlugin extends Serializable { * It is used by the ANNIS plugin system to generate something viewable for vaadin. * */ - public Component createComponent(VisualizerInput visInput, VisualizationToggle visToggle); + public Component createComponent(VisualizerInput visInput, VisualizerPanel visToggle); /** * Get the shorted name of the linguistic type of this visualizer ("partitur", "tree", etc.) diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/AbstractImageVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/AbstractImageVisualizer.java index e8c5e14940..93f20b1674 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/AbstractImageVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/AbstractImageVisualizer.java @@ -19,8 +19,8 @@ import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.util.UUID; -import org.corpus_tools.annis.gui.ImagePanel; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.components.ImagePanel; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; @@ -35,7 +35,7 @@ public abstract class AbstractImageVisualizer extends AbstractVisualizer { private static final long serialVersionUID = -3055099196694903386L; @Override - public ImagePanel createComponent(final VisualizerInput visInput, VisualizationToggle visToggle) { + public ImagePanel createComponent(final VisualizerInput visInput, VisualizerPanel visPanel) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); writeOutput(visInput, out); diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/AudioVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/AudioVisualizer.java index a9b4510eab..7c7f5ec65b 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/AudioVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/AudioVisualizer.java @@ -21,17 +21,18 @@ import com.vaadin.ui.UI; import java.util.List; import org.apache.tika.Tika; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.VisualizationToggle; import org.corpus_tools.annis.gui.components.ExceptionDialog; import org.corpus_tools.annis.gui.components.medialement.MediaElement; import org.corpus_tools.annis.gui.components.medialement.MediaElementPlayer; import org.corpus_tools.annis.gui.media.MediaController; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * @@ -49,7 +50,7 @@ public class AudioVisualizer extends AbstractVisualizer { // NO_UCD (unused code private final Tika tika = new Tika(); @Override - public MediaElementPlayer createComponent(VisualizerInput input, VisualizationToggle visToggle) { + public MediaElementPlayer createComponent(VisualizerInput input, VisualizerPanel visPanel) { List corpusPath = Helper.getCorpusPath(input.getDocument().getGraph(), input.getDocument()); @@ -58,10 +59,14 @@ public MediaElementPlayer createComponent(VisualizerInput input, VisualizationTo String corpusName = corpusPath.get(corpusPath.size() - 1); - CorporaApi api = new CorporaApi(Helper.getClient(input.getUI())); + WebClient client = input.getUI().getWebClient(); try { - List files = - api.listFiles(corpusName, Joiner.on('/').join(Lists.reverse(corpusPath))); + List files = client.get() + .uri(ub -> ub.path("/corpora/{corpus}/files") + .queryParam("node", Joiner.on('/').join(Lists.reverse(corpusPath))).build(corpusName)) + .retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}).block(); + for (String f : files) { String guessedMimeType = tika.detect(f); if (guessedMimeType != null && guessedMimeType.startsWith("audio/")) { @@ -79,7 +84,7 @@ public MediaElementPlayer createComponent(VisualizerInput input, VisualizationTo } } - } catch (ApiException e) { + } catch (WebClientResponseException e) { ExceptionDialog.show(e, UI.getCurrent()); } @@ -89,7 +94,7 @@ public MediaElementPlayer createComponent(VisualizerInput input, VisualizationTo if (VaadinSession.getCurrent().getAttribute(MediaController.class) != null) { VaadinSession.getCurrent().getAttribute(MediaController.class).addMediaPlayer(player, - input.getId(), visToggle); + input.getId(), visPanel); } return player; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/RawTextVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/RawTextVisualizer.java index 45627f5113..86dea2b0fb 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/RawTextVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/RawTextVisualizer.java @@ -23,9 +23,9 @@ import com.vaadin.ui.themes.ValoTheme; import java.util.regex.Pattern; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.VisualizationToggle; import org.corpus_tools.annis.gui.objects.RawTextWrapper; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.salt.common.STextualDS; @@ -65,7 +65,7 @@ public class RawTextVisualizer extends AbstractVisualizer { // NO_UCD (test only private final Pattern whiteSpaceMatcher = Pattern.compile("^\\s+$"); @Override - public Panel createComponent(VisualizerInput visInput, VisualizationToggle visToggle) { + public Panel createComponent(VisualizerInput visInput, VisualizerPanel visPanel) { // get config for alignment boolean vertical = diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/VideoVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/VideoVisualizer.java index 6a3dc974fd..b6d062a685 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/VideoVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/VideoVisualizer.java @@ -21,17 +21,18 @@ import com.vaadin.ui.UI; import java.util.List; import org.apache.tika.Tika; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.VisualizationToggle; import org.corpus_tools.annis.gui.components.ExceptionDialog; import org.corpus_tools.annis.gui.components.medialement.MediaElement; import org.corpus_tools.annis.gui.components.medialement.MediaElementPlayer; import org.corpus_tools.annis.gui.media.MediaController; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * @@ -49,7 +50,7 @@ public class VideoVisualizer extends AbstractVisualizer { // NO_UCD (unused code private final Tika tika = new Tika(); @Override - public MediaElementPlayer createComponent(VisualizerInput input, VisualizationToggle visToggle) { + public MediaElementPlayer createComponent(VisualizerInput input, VisualizerPanel visPanel) { List corpusPath = Helper.getCorpusPath(input.getDocument().getGraph(), input.getDocument()); @@ -58,10 +59,13 @@ public MediaElementPlayer createComponent(VisualizerInput input, VisualizationTo String corpusName = corpusPath.get(corpusPath.size() - 1); - CorporaApi api = new CorporaApi(Helper.getClient(input.getUI())); + WebClient client = input.getUI().getWebClient(); try { - List files = - api.listFiles(corpusName, Joiner.on('/').join(Lists.reverse(corpusPath))); + List files = client.get() + .uri(ub -> ub.path("/corpora/{corpus}/files") + .queryParam("node", Joiner.on('/').join(Lists.reverse(corpusPath))).build(corpusName)) + .retrieve().bodyToMono(new ParameterizedTypeReference>() {}).block(); + for (String f : files) { String guessedMimeType = tika.detect(f); if (guessedMimeType != null && guessedMimeType.startsWith("video/")) { @@ -71,7 +75,7 @@ public MediaElementPlayer createComponent(VisualizerInput input, VisualizationTo mimeType = guessedMimeType; } } - } catch (ApiException e) { + } catch (WebClientResponseException e) { ExceptionDialog.show(e, UI.getCurrent()); } @@ -81,7 +85,7 @@ public MediaElementPlayer createComponent(VisualizerInput input, VisualizationTo if (VaadinSession.getCurrent().getAttribute(MediaController.class) != null) { VaadinSession.getCurrent().getAttribute(MediaController.class).addMediaPlayer(player, - input.getId(), visToggle); + input.getId(), visPanel); } return player; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/EventExtractor.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/EventExtractor.java index 8d3cbe30fa..811356b31b 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/EventExtractor.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/EventExtractor.java @@ -39,10 +39,10 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.PDFPageHelper; import org.corpus_tools.annis.gui.media.PDFController; import org.corpus_tools.annis.gui.media.TimeHelper; +import org.corpus_tools.annis.gui.util.Helper; +import org.corpus_tools.annis.gui.util.PDFPageHelper; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.widgets.grid.GridEvent; import org.corpus_tools.annis.gui.widgets.grid.Row; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/GridVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/GridVisualizer.java index 3db59ccb5d..cc2e9cc974 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/GridVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/GridVisualizer.java @@ -14,9 +14,9 @@ package org.corpus_tools.annis.gui.visualizers.component.grid; import java.util.List; -import org.corpus_tools.annis.gui.VisualizationToggle; import org.corpus_tools.annis.gui.media.MediaController; import org.corpus_tools.annis.gui.media.PDFController; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.salt.common.STextualDS; @@ -58,7 +58,7 @@ public class GridVisualizer extends AbstractVisualizer { // NO_UCD (unused code) private static final Logger log = LoggerFactory.getLogger(GridVisualizer.class); @Override - public GridComponent createComponent(VisualizerInput visInput, VisualizationToggle visToggle) { + public GridComponent createComponent(VisualizerInput visInput, VisualizerPanel visPanel) { MediaController mediaController = visInput.getUI().getSession().getAttribute(MediaController.class); PDFController pdfController = visInput.getUI().getSession().getAttribute(PDFController.class); diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/SingleGridComponent.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/SingleGridComponent.java index 61ac4fbb05..d8f5ae4e3c 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/SingleGridComponent.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/grid/SingleGridComponent.java @@ -33,9 +33,9 @@ import java.util.Set; import java.util.regex.Pattern; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.media.MediaController; import org.corpus_tools.annis.gui.media.PDFController; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.widgets.grid.AnnotationGrid; import org.corpus_tools.annis.gui.widgets.grid.GridEvent; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/gridtree/GridTreeVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/gridtree/GridTreeVisualizer.java index 35d9ddf884..370b5c078d 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/gridtree/GridTreeVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/gridtree/GridTreeVisualizer.java @@ -25,8 +25,8 @@ import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.widgets.grid.AnnotationGrid; @@ -78,7 +78,7 @@ private static class GridTreePanel extends Panel { private VisualizerInput input; private SDocumentGraph graph; - public GridTreePanel(VisualizerInput visInput, VisualizationToggle visToggle) { + public GridTreePanel(VisualizerInput visInput, VisualizerPanel visPanel) { // nothing to render if no input is there if (visInput == null) { @@ -376,8 +376,8 @@ public void nodeReached(GRAPH_TRAVERSE_TYPE g, String string, SNode currNode, SR private static final long serialVersionUID = 2295569067592940440L; @Override - public Panel createComponent(VisualizerInput visInput, VisualizationToggle visToggle) { - return new GridTreePanel(visInput, visToggle); + public Panel createComponent(VisualizerInput visInput, VisualizerPanel visPanel) { + return new GridTreePanel(visInput, visPanel); } @Override diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/kwic/KWICVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/kwic/KWICVisualizer.java index 0fb6638a0b..17a8701f63 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/kwic/KWICVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/kwic/KWICVisualizer.java @@ -17,9 +17,9 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.corpus_tools.annis.gui.VisualizationToggle; import org.corpus_tools.annis.gui.media.MediaController; import org.corpus_tools.annis.gui.media.PDFController; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.salt.common.STextualDS; @@ -43,7 +43,7 @@ public class KWICVisualizer extends AbstractVisualizer { // NO_UCD (unused code) private static final long serialVersionUID = 1405603777567084847L; @Override - public KWICInterface createComponent(VisualizerInput visInput, VisualizationToggle visToggle) { + public KWICInterface createComponent(VisualizerInput visInput, VisualizerPanel visPanel) { MediaController mediaController = VaadinSession.getCurrent().getAttribute(MediaController.class); PDFController pdfController = VaadinSession.getCurrent().getAttribute(PDFController.class); diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFFullVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFFullVisualizer.java index c03964a1ec..d60e9e1a79 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFFullVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFFullVisualizer.java @@ -14,7 +14,7 @@ package org.corpus_tools.annis.gui.visualizers.component.pdf; import com.vaadin.ui.Panel; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.springframework.stereotype.Component; @@ -40,7 +40,7 @@ public class PDFFullVisualizer extends AbstractVisualizer { // NO_UCD (unused co private static final long serialVersionUID = -1053564254980292442L; @Override - public Panel createComponent(VisualizerInput input, VisualizationToggle visToggle) { + public Panel createComponent(VisualizerInput input, VisualizerPanel visPanel) { Panel p = new Panel(); p.setHeight(input.getMappings().getOrDefault("height", "-1") + "px"); p.setContent(new PDFPanel(input, "-1")); diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFPanel.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFPanel.java index 4a6aab8981..8437f4f711 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFPanel.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFPanel.java @@ -13,10 +13,11 @@ */ package org.corpus_tools.annis.gui.visualizers.component.pdf; -import static org.corpus_tools.annis.gui.PDFPageHelper.PAGE_NO_VALID_NUMBER; -import static org.corpus_tools.annis.gui.PDFPageHelper.PAGE_NUMBER_SEPERATOR; +import static org.corpus_tools.annis.gui.util.PDFPageHelper.PAGE_NO_VALID_NUMBER; +import static org.corpus_tools.annis.gui.util.PDFPageHelper.PAGE_NUMBER_SEPERATOR; import com.google.common.base.Joiner; +import com.google.common.collect.Lists; import com.google.common.escape.Escaper; import com.google.common.net.UrlEscapers; import com.vaadin.annotations.JavaScript; @@ -24,11 +25,12 @@ import java.util.Collections; import java.util.List; import java.util.UUID; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.components.ExceptionDialog; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * Inits the wrapper for the pdf visualization. Neccesary steps for this are: @@ -87,13 +89,13 @@ public void attach() { setSizeUndefined(); // set the state - getState().binaryURL = getBinaryPath(new CorporaApi(Helper.getClient(input.getUI()))); + getState().binaryURL = getBinaryPath(input.getUI().getWebClient()); getState().pdfID = getPDF_ID(); getState().firstPage = firstPage; getState().lastPage = lastPage; } - protected String getBinaryPath(CorporaApi api) { + protected String getBinaryPath(WebClient client) { List corpusPath = Helper.getCorpusPath(input.getDocument().getGraph(), input.getDocument()); @@ -101,7 +103,11 @@ protected String getBinaryPath(CorporaApi api) { String corpusName = corpusPath.get(0); try { - List files = api.listFiles(corpusName, Joiner.on('/').join(corpusPath)); + List files = client.get() + .uri(ub -> ub.path("/corpora/{corpus}/files") + .queryParam("node", Joiner.on('/').join(Lists.reverse(corpusPath))).build(corpusName)) + .retrieve().bodyToMono(new ParameterizedTypeReference>() {}).block(); + for (String f : files) { if (f.endsWith(".pdf")) { // Create an URL how to featch the PDF file @@ -109,7 +115,7 @@ protected String getBinaryPath(CorporaApi api) { + urlParamEscape.escape(corpusName) + "&file=" + urlParamEscape.escape(f); } } - } catch (ApiException e) { + } catch (WebClientResponseException e) { ExceptionDialog.show(e, input.getUI()); } diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFVisualizer.java index 0e8f8b9c93..8947cae38e 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFVisualizer.java @@ -13,14 +13,14 @@ */ package org.corpus_tools.annis.gui.visualizers.component.pdf; -import static org.corpus_tools.annis.gui.PDFPageHelper.PAGE_NO_VALID_NUMBER; +import static org.corpus_tools.annis.gui.util.PDFPageHelper.PAGE_NO_VALID_NUMBER; import com.vaadin.server.VaadinSession; import com.vaadin.ui.Panel; -import org.corpus_tools.annis.gui.PDFPageHelper; -import org.corpus_tools.annis.gui.VisualizationToggle; import org.corpus_tools.annis.gui.media.PDFController; import org.corpus_tools.annis.gui.media.PDFViewer; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; +import org.corpus_tools.annis.gui.util.PDFPageHelper; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.visualizers.component.grid.GridVisualizer; @@ -59,12 +59,12 @@ private static class PDFViewerImpl extends Panel implements PDFViewer { VisualizerInput input; - VisualizationToggle visToggle; + VisualizerPanel visPanel; PDFPanel pdfPanel; - public PDFViewerImpl(VisualizerInput input, VisualizationToggle visToggle) { - this.visToggle = visToggle; + public PDFViewerImpl(VisualizerInput input, VisualizerPanel visPanel) { + this.visPanel = visPanel; this.input = input; } @@ -93,7 +93,7 @@ public void openPDFPage(String page) { if (!this.isVisible()) { // set visible status this.setVisible(true); - visToggle.toggleVisualizer(true, null); + visPanel.toggleVisualizer(true, null); } } @@ -107,7 +107,7 @@ public void openPDFPage(String page) { private final Logger log = LoggerFactory.getLogger(PDFVisualizer.class); @Override - public Panel createComponent(VisualizerInput input, VisualizationToggle visToggle) { + public Panel createComponent(VisualizerInput input, VisualizerPanel visPanel) { PDFViewer pdfViewer = null; @@ -117,7 +117,7 @@ public Panel createComponent(VisualizerInput input, VisualizationToggle visToggl VaadinSession session = VaadinSession.getCurrent(); PDFController pdfController = session.getAttribute(PDFController.class); - pdfViewer = new PDFViewerImpl(input, visToggle); + pdfViewer = new PDFViewerImpl(input, visPanel); pdfController.addPDF(input.getId(), pdfViewer); } diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RST.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RST.java index 17010c0b74..6aec1cfb20 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RST.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RST.java @@ -13,7 +13,7 @@ */ package org.corpus_tools.annis.gui.visualizers.component.rst; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.springframework.stereotype.Component; @@ -66,7 +66,7 @@ public class RST extends AbstractVisualizer { // NO_UCD (unused code) private static final long serialVersionUID = -1802523184215051640L; @Override - public RSTPanel createComponent(VisualizerInput visInput, VisualizationToggle visToggle) { + public RSTPanel createComponent(VisualizerInput visInput, VisualizerPanel visPanel) { return new RSTPanel(visInput); } diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RSTFull.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RSTFull.java index 69cc5dc14f..6a1784c44f 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RSTFull.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RSTFull.java @@ -13,7 +13,7 @@ */ package org.corpus_tools.annis.gui.visualizers.component.rst; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.springframework.stereotype.Component; @@ -34,7 +34,7 @@ public class RSTFull extends AbstractVisualizer { // NO_UCD (unused code) private static final long serialVersionUID = 1112108378282427967L; @Override - public RSTPanel createComponent(VisualizerInput visInput, VisualizationToggle visToggle) { + public RSTPanel createComponent(VisualizerInput visInput, VisualizerPanel visPanel) { return new RSTPanel(visInput); } diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RSTImpl.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RSTImpl.java index 1eda61be56..593ca09b0f 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RSTImpl.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/rst/RSTImpl.java @@ -29,9 +29,9 @@ import java.util.Stack; import java.util.TreeSet; import java.util.UUID; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.MatchedNodeColors; import org.corpus_tools.annis.gui.components.CssRenderInfo; +import org.corpus_tools.annis.gui.objects.MatchedNodeColors; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.widgets.JITWrapper; import org.corpus_tools.annis.gui.widgets.gwt.client.ui.VJITWrapper; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/sentstructurejs/SentStructureJs.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/sentstructurejs/SentStructureJs.java index 655ae2cce8..090d824a6c 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/sentstructurejs/SentStructureJs.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/sentstructurejs/SentStructureJs.java @@ -1,7 +1,7 @@ package org.corpus_tools.annis.gui.visualizers.component.sentstructurejs; import com.vaadin.ui.Panel; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.springframework.stereotype.Component; @@ -12,7 +12,7 @@ public class SentStructureJs extends AbstractVisualizer { // NO_UCD (unused code private static final long serialVersionUID = -5677329079488473862L; @Override - public Panel createComponent(VisualizerInput visInput, VisualizationToggle visToggle) { + public Panel createComponent(VisualizerInput visInput, VisualizerPanel visPanel) { SentStructureJsPanel panel = new SentStructureJsPanel(visInput); panel.setHeight("100%"); panel.setWidth("100%"); diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/sentstructurejs/SentStructureJsComponent.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/sentstructurejs/SentStructureJsComponent.java index 59a86e8f5b..09afbd1ad2 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/sentstructurejs/SentStructureJsComponent.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/sentstructurejs/SentStructureJsComponent.java @@ -17,7 +17,7 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import org.corpus_tools.annis.gui.MatchedNodeColors; +import org.corpus_tools.annis.gui.objects.MatchedNodeColors; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.visualizers.component.grid.EventExtractor; import org.corpus_tools.salt.common.SDocument; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/tree/AnnisGraphTools.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/tree/AnnisGraphTools.java index 9edc3e1c1f..0bbcd29547 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/tree/AnnisGraphTools.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/tree/AnnisGraphTools.java @@ -24,7 +24,7 @@ import java.util.List; import java.util.Set; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.salt.SALT_TYPE; import org.corpus_tools.salt.common.SDocumentGraph; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/tree/TigerTreeVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/tree/TigerTreeVisualizer.java index ead93ca815..de9b8a22a7 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/tree/TigerTreeVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/tree/TigerTreeVisualizer.java @@ -36,8 +36,8 @@ import java.util.Set; import javax.imageio.ImageIO; import org.apache.commons.io.FileUtils; -import org.corpus_tools.annis.gui.MatchedNodeColors; import org.corpus_tools.annis.gui.objects.AnnisConstants; +import org.corpus_tools.annis.gui.objects.MatchedNodeColors; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.visualizers.component.AbstractImageVisualizer; import org.corpus_tools.annis.gui.visualizers.component.tree.backends.staticimg.AbstractImageGraphicsItem; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJs.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJs.java index 7e525c549f..059b7512d8 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJs.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJs.java @@ -2,7 +2,7 @@ import com.vaadin.ui.Panel; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.springframework.stereotype.Component; @@ -19,7 +19,7 @@ public class VisJs extends AbstractVisualizer { // NO_UCD (unused code) private static final long serialVersionUID = 6223666444705431088L; @Override - public Panel createComponent(VisualizerInput visInput, VisualizationToggle visToggle) { + public Panel createComponent(VisualizerInput visInput, VisualizerPanel visPanel) { VisJsPanel panel = new VisJsPanel(visInput); return panel; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJsComponent.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJsComponent.java index 7f5875e714..986066efe1 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJsComponent.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJsComponent.java @@ -20,7 +20,7 @@ import java.util.TreeSet; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.gui.MatchedNodeColors; +import org.corpus_tools.annis.gui.objects.MatchedNodeColors; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.visualizers.component.grid.EventExtractor; import org.corpus_tools.salt.common.SDocument; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJsDoc.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJsDoc.java index d27dfe9d28..a5f73a9227 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJsDoc.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/component/visjs/VisJsDoc.java @@ -1,7 +1,7 @@ package org.corpus_tools.annis.gui.visualizers.component.visjs; import com.vaadin.ui.Panel; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.springframework.stereotype.Component; @@ -13,7 +13,7 @@ public class VisJsDoc extends AbstractVisualizer { // NO_UCD (unused code) private static final long serialVersionUID = -4818088208741889964L; @Override - public Panel createComponent(VisualizerInput visInput, VisualizationToggle visToggle) { + public Panel createComponent(VisualizerInput visInput, VisualizerPanel visPanel) { VisJsPanel panel = new VisJsPanel(visInput); return panel; } diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/htmlvis/HTMLVis.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/htmlvis/HTMLVis.java index fa2ee09fa6..2ac1816fb1 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/htmlvis/HTMLVis.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/htmlvis/HTMLVis.java @@ -26,12 +26,9 @@ import com.vaadin.ui.TextArea; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.file.Files; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -45,12 +42,11 @@ import java.util.TreeMap; import java.util.TreeSet; import org.apache.commons.io.IOUtils; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; import org.corpus_tools.annis.gui.AnnisBaseUI; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.MatchedNodeColors; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.CommonUI; +import org.corpus_tools.annis.gui.objects.MatchedNodeColors; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.salt.common.SDocumentGraph; @@ -60,7 +56,10 @@ import org.corpus_tools.salt.core.SNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * @@ -92,7 +91,7 @@ public class HTMLVis extends AbstractVisualizer { private boolean hitMark = true; @Override - public Panel createComponent(VisualizerInput vi, VisualizationToggle vt) { + public Panel createComponent(VisualizerInput vi, VisualizerPanel visPanel) { Panel scrollPanel = new Panel(); scrollPanel.setSizeFull(); Label lblResult = new Label("ERROR", ContentMode.HTML); @@ -128,7 +127,7 @@ public Panel createComponent(VisualizerInput vi, VisualizationToggle vt) { lblResult.addStyleName(labelClass); injectWebFonts(visConfigName, corpusName, rootCorpusId, vi.getUI(), - new CorporaApi(Helper.getClient(vi.getUI()))); + vi.getUI().getWebClient()); injectCSS(visConfigName, corpusName, rootCorpusId, wrapperClassName, vi.getUI()); } @@ -389,7 +388,7 @@ private JsonMapper createJsonMapper() { @Override public List getFilteredNodeAnnotationNames(String toplevelCorpusName, - String toplevelCorpusId, Map mappings, UI ui) { + String toplevelCorpusId, Map mappings, CommonUI ui) { Set result = null; toplevelCorpusId = Helper.removeSaltPrefix(toplevelCorpusId); @@ -426,20 +425,20 @@ public String getShortName() { } private void injectCSS(String visConfigName, String corpusName, String corpusNodeId, - String wrapperClassName, UI ui) { - CorporaApi api = new CorporaApi(Helper.getClient(ui)); + String wrapperClassName, CommonUI ui) { + WebClient client = ui.getWebClient(); InputStream inStreamCSSRaw = null; if (visConfigName == null) { inStreamCSSRaw = HTMLVis.class.getResourceAsStream("htmlvis.css"); } else { try { - File f = api.getFile(corpusName, corpusNodeId + "/" + visConfigName + ".css"); - f.deleteOnExit(); - - inStreamCSSRaw = new FileInputStream(f); - - } catch (ApiException ex) { - if (ex.getCode() != 404) { + byte[] response = client.get() + .uri("/corpora/{corpus}/files/{name}", corpusName, + corpusNodeId + "/" + visConfigName + ".css") + .retrieve().bodyToMono(byte[].class).block(); + inStreamCSSRaw = new ByteArrayInputStream(response); + } catch (WebClientResponseException ex) { + if (ex.getStatusCode() != HttpStatus.NOT_FOUND) { log.error("Could not retrieve the HTML visualizer web-font configuration file", ex); ui.access(() -> { Notification.show("Could not retrieve the HTML visualizer web-font configuration file", @@ -447,8 +446,6 @@ private void injectCSS(String visConfigName, String corpusName, String corpusNod }); } - } catch (FileNotFoundException ex) { - log.error("Just downloaded file not found", ex); } } @@ -467,31 +464,31 @@ private void injectCSS(String visConfigName, String corpusName, String corpusNod } } - protected void injectWebFonts(String visConfigName, String corpusName, String corpusNodeId, UI ui, - CorporaApi api) { + protected void injectWebFonts(String visConfigName, String corpusName, String corpusNodeId, + UI ui, WebClient client) { try { - File f = api.getFile(corpusName, corpusNodeId + "/" + visConfigName + ".fonts.json"); - f.deleteOnExit(); - try (FileInputStream inStreamJSON = new FileInputStream(f)) { + byte[] response = client.get() + .uri("/corpora/{corpus}/files/{name}", corpusName, + corpusNodeId + "/" + visConfigName + ".fonts.json") + .retrieve().bodyToMono(byte[].class).block(); + + try (ByteArrayInputStream inStreamJSON = new ByteArrayInputStream(response)) { ObjectMapper mapper = createJsonMapper(); WebFontList fontConfigList = mapper.readValue(inStreamJSON, WebFontList.class); for (WebFont fontConfig : fontConfigList.getWebFonts()) { injectWebFontConfig(fontConfig, ui); } - } catch (IOException ex) { log.error("Could not parse the HTML visualizer web-font configuration file", ex); new Notification("Could not parse the HTML visualizer web-font configuration file", ex.getMessage(), Notification.Type.ERROR_MESSAGE).show(ui.getPage()); - } finally { - Files.deleteIfExists(f.toPath()); } - } catch (IOException ex) { - log.error("Unexpected input/output exception", ex); - } catch (ApiException ex) { - if (ex.getCode() != 404) { + + + } catch (WebClientResponseException ex) { + if (ex.getStatusCode() != HttpStatus.NOT_FOUND) { log.error("Could not retrieve the HTML visualizer web-font configuration file", ex); ui.access(() -> { new Notification("Could not retrieve the HTML visualizer web-font configuration file", @@ -537,7 +534,7 @@ public boolean isUsingText() { } private VisualizationDefinition[] parseDefinitions(String corpusName, String corpusNodeId, - Map mappings, UI ui) { + Map mappings, CommonUI ui) { InputStream inStreamConfigRaw = null; String visConfigName = mappings.get("config"); @@ -546,16 +543,18 @@ private VisualizationDefinition[] parseDefinitions(String corpusName, String cor inStreamConfigRaw = HTMLVis.class.getResourceAsStream("defaultvis.config"); } else { - CorporaApi api = new CorporaApi(Helper.getClient(ui)); + WebClient client = ui.getWebClient(); try { - File file = api.getFile(corpusName, corpusNodeId + "/" + visConfigName + ".config"); - inStreamConfigRaw = new FileInputStream(file); - } catch (ApiException e) { - if (e.getCode() != 404) { + byte[] response = client.get() + .uri("/corpora/{corpus}/files/{name}", corpusName, + corpusNodeId + "/" + visConfigName + ".config") + .retrieve().bodyToMono(byte[].class).block(); + + inStreamConfigRaw = new ByteArrayInputStream(response); + } catch (WebClientResponseException e) { + if (e.getStatusCode() != HttpStatus.NOT_FOUND) { log.error("Exception while getting the HTML visualizer configuration", e); } - } catch (IOException e) { - log.error("Exception while getting the HTML visualizer configuration", e); } } diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/htmlvis/SpanHTMLOutputter.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/htmlvis/SpanHTMLOutputter.java index 2f8bb7d8c8..08205d959a 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/htmlvis/SpanHTMLOutputter.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/htmlvis/SpanHTMLOutputter.java @@ -21,7 +21,7 @@ import java.util.List; import java.util.Map; import java.util.SortedMap; -import org.corpus_tools.annis.gui.Helper; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.salt.common.SDocumentGraph; import org.corpus_tools.salt.common.SSpan; import org.corpus_tools.salt.common.SStructuredNode; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/AbstractIFrameVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/AbstractIFrameVisualizer.java index 2a4b721b4d..947da8d0dc 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/AbstractIFrameVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/AbstractIFrameVisualizer.java @@ -19,7 +19,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.UUID; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.AbstractVisualizer; import org.corpus_tools.annis.gui.visualizers.IFrameResource; import org.corpus_tools.annis.gui.visualizers.IFrameResourceMap; @@ -44,7 +44,7 @@ public abstract class AbstractIFrameVisualizer extends AbstractVisualizer org.slf4j.LoggerFactory.getLogger(AbstractIFrameVisualizer.class); @Override - public Component createComponent(final VisualizerInput vis, VisualizationToggle visToggle) { + public Component createComponent(final VisualizerInput vis, VisualizerPanel visPanel) { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); writeOutput(vis, outStream); diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/CorefVisualizer.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/CorefVisualizer.java index 43a3bc41e5..8db48a0893 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/CorefVisualizer.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/CorefVisualizer.java @@ -35,8 +35,8 @@ import java.util.Set; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.MatchedNodeColors; +import org.corpus_tools.annis.gui.objects.MatchedNodeColors; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.common.SDocumentGraph; diff --git a/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/dependency/VakyarthaDependencyTree.java b/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/dependency/VakyarthaDependencyTree.java index 9c68fe0871..d082ac8c2d 100644 --- a/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/dependency/VakyarthaDependencyTree.java +++ b/src/main/java/org/corpus_tools/annis/gui/visualizers/iframe/dependency/VakyarthaDependencyTree.java @@ -25,8 +25,8 @@ import java.util.Set; import java.util.TreeSet; import org.apache.commons.lang3.StringUtils; -import org.corpus_tools.annis.gui.Helper; -import org.corpus_tools.annis.gui.MatchedNodeColors; +import org.corpus_tools.annis.gui.objects.MatchedNodeColors; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.annis.gui.visualizers.iframe.WriterVisualizer; import org.corpus_tools.salt.SALT_TYPE; diff --git a/src/main/java/org/corpus_tools/annis/gui/widgets/grid/AnnotationGrid.java b/src/main/java/org/corpus_tools/annis/gui/widgets/grid/AnnotationGrid.java index e107e643f7..5efbed9148 100644 --- a/src/main/java/org/corpus_tools/annis/gui/widgets/grid/AnnotationGrid.java +++ b/src/main/java/org/corpus_tools/annis/gui/widgets/grid/AnnotationGrid.java @@ -22,9 +22,9 @@ import java.util.Collections; import java.util.Map; import java.util.Set; -import org.corpus_tools.annis.gui.MatchedNodeColors; import org.corpus_tools.annis.gui.media.MediaController; import org.corpus_tools.annis.gui.media.PDFController; +import org.corpus_tools.annis.gui.objects.MatchedNodeColors; /** * diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6a0391fc9a..89898a2be5 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,6 +9,7 @@ vaadin.servlet.productionMode=true # Path to the persistent database, where e.g. the reference links are stored spring.datasource.url=jdbc:h2:file:${user.home}/.annis/v4/frontend_data.h2 +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect # Automatically create and update the schema spring.jpa.generate-ddl=true diff --git a/src/main/resources/openapi.yml b/src/main/resources/openapi.yml index 104910136a..9a3ebb76ef 100644 --- a/src/main/resources/openapi.yml +++ b/src/main/resources/openapi.yml @@ -123,30 +123,20 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Job" + type: string "200": - description: Job was finished successfully and result can be downloaded from the body - "303": - description: Job was finished successfully + description: Job was finished successfully. The body contains the messages from the import. content: application/json: schema: - type: array + type: string description: The messages produced by the background job. - example: - [ - "started import of corpus GUM", - "reading GraphML", - "Error during import of GUM: corpus already exists", - ] - items: - type: string "410": description: Job failed content: application/json: schema: - $ref: "#/components/schemas/Job" + type: string "404": description: Job not found diff --git a/src/test/java/org/corpus_tools/annis/gui/AnnisUITest.java b/src/test/java/org/corpus_tools/annis/gui/AnnisUITest.java index 3e0f8053c4..4d9d84fd2b 100644 --- a/src/test/java/org/corpus_tools/annis/gui/AnnisUITest.java +++ b/src/test/java/org/corpus_tools/annis/gui/AnnisUITest.java @@ -55,6 +55,7 @@ import org.corpus_tools.annis.gui.controlpanel.SearchOptionsPanel; import org.corpus_tools.annis.gui.docbrowser.DocBrowserPanel; import org.corpus_tools.annis.gui.docbrowser.DocBrowserTable; +import org.corpus_tools.annis.gui.objects.CorpusSet; import org.corpus_tools.annis.gui.resultview.ResultViewPanel; import org.corpus_tools.annis.gui.resultview.SingleCorpusResultPanel; import org.corpus_tools.annis.gui.resultview.SingleResultPanel; diff --git a/src/test/java/org/corpus_tools/annis/gui/CorpusBrowserPanelTest.java b/src/test/java/org/corpus_tools/annis/gui/CorpusBrowserPanelTest.java index cac44c45d8..cb7dbd61ed 100644 --- a/src/test/java/org/corpus_tools/annis/gui/CorpusBrowserPanelTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/CorpusBrowserPanelTest.java @@ -19,8 +19,10 @@ import java.util.HashSet; import java.util.Map; import java.util.stream.Collectors; -import org.corpus_tools.annis.gui.beans.CorpusBrowserEntry; +import org.corpus_tools.annis.gui.components.ExampleTable; import org.corpus_tools.annis.gui.controlpanel.CorpusListPanel; +import org.corpus_tools.annis.gui.corpusbrowser.CorpusBrowserEntry; +import org.corpus_tools.annis.gui.corpusbrowser.CorpusBrowserPanel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -61,7 +63,7 @@ private void openCorpusBrowser(String corpus) throws Exception { // Open a corpus browser for the given corpus Button button = mock(Button.class); CorpusListPanel corpusList = ui.getSearchView().getControlPanel().getCorpusList(); - corpusList.initCorpusBrowser(corpus, button); + corpusList.initCorpusBrowser(corpus, button, ui); // Get window by its title Window w = _get(Window.class, spec -> spec.withCaption("Corpus information for " + corpus)); diff --git a/src/test/java/org/corpus_tools/annis/gui/ExportPanelTest.java b/src/test/java/org/corpus_tools/annis/gui/ExportPanelTest.java index 13fe4161e0..ad43be5d59 100644 --- a/src/test/java/org/corpus_tools/annis/gui/ExportPanelTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/ExportPanelTest.java @@ -16,7 +16,9 @@ import com.vaadin.ui.Notification; import com.vaadin.ui.TextField; import kotlin.Pair; +import org.corpus_tools.annis.gui.components.ExportPanel; import org.corpus_tools.annis.gui.controlpanel.SearchOptionsPanel; +import org.corpus_tools.annis.gui.objects.QueryGenerator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/corpus_tools/annis/gui/HelperTest.java b/src/test/java/org/corpus_tools/annis/gui/HelperTest.java index d899646df2..fda55343a8 100644 --- a/src/test/java/org/corpus_tools/annis/gui/HelperTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/HelperTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; +import org.corpus_tools.annis.gui.util.Helper; import org.junit.jupiter.api.Test; class HelperTest { diff --git a/src/test/java/org/corpus_tools/annis/gui/MetaDataPanelTest.java b/src/test/java/org/corpus_tools/annis/gui/MetaDataPanelTest.java index ff4499ff5d..3f2954cec1 100644 --- a/src/test/java/org/corpus_tools/annis/gui/MetaDataPanelTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/MetaDataPanelTest.java @@ -17,6 +17,7 @@ import com.vaadin.ui.ProgressBar; import com.vaadin.ui.Window; import org.corpus_tools.annis.api.model.Annotation; +import org.corpus_tools.annis.gui.components.MetaDataPanel; import org.corpus_tools.annis.gui.controlpanel.CorpusListPanel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -54,7 +55,7 @@ void showPcc2CorpusBrowser() throws Exception { // Open a corpus browser for pcc2 Button button = mock(Button.class); CorpusListPanel corpusList = ui.getSearchView().getControlPanel().getCorpusList(); - corpusList.initCorpusBrowser("pcc2", button); + corpusList.initCorpusBrowser("pcc2", button, ui); // Get window by its title Window w = _get(Window.class, spec -> spec.withCaption("Corpus information for pcc2")); diff --git a/src/test/java/org/corpus_tools/annis/gui/PDFPageHelperTest.java b/src/test/java/org/corpus_tools/annis/gui/PDFPageHelperTest.java index 263d8b1bed..cc42ec2521 100644 --- a/src/test/java/org/corpus_tools/annis/gui/PDFPageHelperTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/PDFPageHelperTest.java @@ -5,7 +5,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import org.corpus_tools.annis.gui.PDFPageHelper; +import org.corpus_tools.annis.gui.util.PDFPageHelper; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; import org.corpus_tools.salt.SaltFactory; import org.corpus_tools.salt.common.SDocument; diff --git a/src/test/java/org/corpus_tools/annis/gui/QueryControllerTest.java b/src/test/java/org/corpus_tools/annis/gui/QueryControllerTest.java index 6a9dbe0ea6..da71ae44ad 100644 --- a/src/test/java/org/corpus_tools/annis/gui/QueryControllerTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/QueryControllerTest.java @@ -9,6 +9,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.corpus_tools.annis.gui.controller.QueryController; import org.corpus_tools.annis.gui.exporter.CSVExporter; import org.corpus_tools.annis.gui.exporter.TextColumnExporter; import org.corpus_tools.annis.gui.objects.ExportQuery; diff --git a/src/test/java/org/corpus_tools/annis/gui/ServiceStarterDesktopTest.java b/src/test/java/org/corpus_tools/annis/gui/ServiceStarterDesktopTest.java index 7c5b0ff991..f93d04221a 100644 --- a/src/test/java/org/corpus_tools/annis/gui/ServiceStarterDesktopTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/ServiceStarterDesktopTest.java @@ -15,6 +15,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.web.WebAppConfiguration; @@ -44,16 +45,15 @@ public void tearDown() { @Test void testDesktopAuthConfigured() { - assertNotNull(ui.getAuthListener().getToken()); - SecurityContext ctx = ui.getSecurityContext(); + SecurityContext ctx = SecurityContextHolder.getContext(); assertNotNull(ctx); Authentication auth = ctx.getAuthentication(); assertNotNull(auth); + assertNotNull(auth.getCredentials()); assertTrue(auth.isAuthenticated()); assertEquals("desktop", auth.getName()); assertNull(auth.getDetails()); - assertEquals(ui.getAuthListener().getToken(), auth.getCredentials()); assertTrue(auth.getPrincipal() instanceof OAuth2User); } diff --git a/src/test/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationPanelTest.java b/src/test/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationPanelTest.java index c40d6debae..d06397bf63 100644 --- a/src/test/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationPanelTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/admin/reflinks/MigrationPanelTest.java @@ -34,10 +34,10 @@ import okhttp3.mockwebserver.MockWebServer; import org.apache.commons.io.IOUtils; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.SingletonBeanStoreRetrievalStrategy; import org.corpus_tools.annis.gui.TestHelper; import org.corpus_tools.annis.gui.security.SecurityConfiguration; +import org.corpus_tools.annis.gui.util.Helper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -48,6 +48,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.core.oidc.OidcIdToken; import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.core.user.OAuth2User; @@ -460,15 +461,15 @@ void testFallbackToQuirksMode() throws Exception { @Test void testLoggedOut() { // Test that the test environment provides us with a logged in administrator user first - Authentication oldAuth = ui.getSecurityContext().getAuthentication(); - Optional user = Helper.getUser(ui); + Authentication oldAuth = SecurityContextHolder.getContext().getAuthentication(); + Optional user = Helper.getUser(); assertTrue(user.isPresent()); OAuth2User adminUser = user.get(); assertTrue(Helper.getUserRoles(adminUser).contains("admin")); // Logout the current user - ui.getSecurityContext().setAuthentication(null); - assertFalse(Helper.getUser(ui).isPresent()); + SecurityContextHolder.getContext().setAuthentication(null); + assertFalse(Helper.getUser().isPresent()); // Change to migration tab panel showMigrationPanel(); @@ -476,12 +477,12 @@ void testLoggedOut() { assertNull(panel.getContent()); // Login in the admin user again - ui.getSecurityContext().setAuthentication(oldAuth); + SecurityContextHolder.getContext().setAuthentication(oldAuth); } @Test void testNotAnAdminUser() { - Authentication oldAuth = ui.getSecurityContext().getAuthentication(); + Authentication oldAuth = SecurityContextHolder.getContext().getAuthentication(); // Change the current user to not have the administration role List roles = Arrays.asList(); Instant issuedAt = Instant.now(); @@ -498,7 +499,7 @@ void testNotAnAdminUser() { claims.put("sub", "non-admin"); OidcIdToken token = new OidcIdToken(signedToken, issuedAt, expiresAt, claims); DefaultOidcUser newUser = new DefaultOidcUser(grantedAuthorities, token); - ui.getSecurityContext().setAuthentication( + SecurityContextHolder.getContext().setAuthentication( new UsernamePasswordAuthenticationToken(newUser, signedToken, grantedAuthorities)); @@ -508,7 +509,7 @@ void testNotAnAdminUser() { assertNull(panel.getContent()); // Login in the admin user again - ui.getSecurityContext().setAuthentication(oldAuth); + SecurityContextHolder.getContext().setAuthentication(oldAuth); } } diff --git a/src/test/java/org/corpus_tools/annis/gui/flatquerybuilder/FlatQueryBuilderTest.java b/src/test/java/org/corpus_tools/annis/gui/querybuilder/flat/FlatQueryBuilderTest.java similarity index 93% rename from src/test/java/org/corpus_tools/annis/gui/flatquerybuilder/FlatQueryBuilderTest.java rename to src/test/java/org/corpus_tools/annis/gui/querybuilder/flat/FlatQueryBuilderTest.java index c5cdd1ad8b..28b3997133 100644 --- a/src/test/java/org/corpus_tools/annis/gui/flatquerybuilder/FlatQueryBuilderTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/querybuilder/flat/FlatQueryBuilderTest.java @@ -1,4 +1,4 @@ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import static com.github.mvysny.kaributesting.v8.LocatorJ._click; import static com.github.mvysny.kaributesting.v8.LocatorJ._find; @@ -24,7 +24,15 @@ import java.util.List; import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.SingletonBeanStoreRetrievalStrategy; -import org.corpus_tools.annis.gui.querybuilder.QueryBuilderChooser; +import org.corpus_tools.annis.gui.querybuilder.flat.EdgeBox; +import org.corpus_tools.annis.gui.querybuilder.flat.FlatQueryBuilder; +import org.corpus_tools.annis.gui.querybuilder.flat.MetaBox; +import org.corpus_tools.annis.gui.querybuilder.flat.SearchBox; +import org.corpus_tools.annis.gui.querybuilder.flat.SensitiveComboBox; +import org.corpus_tools.annis.gui.querybuilder.flat.SpanBox; +import org.corpus_tools.annis.gui.querybuilder.flat.ValueField; +import org.corpus_tools.annis.gui.querybuilder.flat.VerticalNode; +import org.corpus_tools.annis.gui.querybuilder.tiger.QueryBuilderChooser; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/corpus_tools/annis/gui/flatquerybuilder/ReducingStringComparatorTest.java b/src/test/java/org/corpus_tools/annis/gui/querybuilder/flat/ReducingStringComparatorTest.java similarity index 91% rename from src/test/java/org/corpus_tools/annis/gui/flatquerybuilder/ReducingStringComparatorTest.java rename to src/test/java/org/corpus_tools/annis/gui/querybuilder/flat/ReducingStringComparatorTest.java index e531301b3f..5bb5c7173c 100644 --- a/src/test/java/org/corpus_tools/annis/gui/flatquerybuilder/ReducingStringComparatorTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/querybuilder/flat/ReducingStringComparatorTest.java @@ -1,10 +1,10 @@ -package org.corpus_tools.annis.gui.flatquerybuilder; +package org.corpus_tools.annis.gui.querybuilder.flat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.corpus_tools.annis.gui.flatquerybuilder.ReducingStringComparator; +import org.corpus_tools.annis.gui.querybuilder.flat.ReducingStringComparator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/corpus_tools/annis/gui/util/TimelineReconstructorTest.java b/src/test/java/org/corpus_tools/annis/gui/util/TimelineReconstructorTest.java index 11a0665e59..c1df6d84fe 100644 --- a/src/test/java/org/corpus_tools/annis/gui/util/TimelineReconstructorTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/util/TimelineReconstructorTest.java @@ -13,7 +13,6 @@ import java.util.Set; import java.util.TreeSet; import javax.xml.stream.XMLStreamException; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.graphml.DocumentGraphMapper; import org.corpus_tools.salt.common.SDocumentGraph; import org.corpus_tools.salt.common.SSpanningRelation; diff --git a/src/test/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFPanelTest.java b/src/test/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFPanelTest.java index 51016677c1..8d35c54a48 100644 --- a/src/test/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFPanelTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/visualizers/component/pdf/PDFPanelTest.java @@ -2,32 +2,45 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.awt.FontFormatException; import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedList; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; import org.corpus_tools.annis.gui.AnnisUI; import org.corpus_tools.annis.gui.components.ExceptionDialog; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; -import org.corpus_tools.annis.gui.visualizers.component.pdf.PDFPanel; import org.corpus_tools.salt.common.SCorpusGraph; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.samples.SampleGenerator; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; class PDFPanelTest { VisualizerInput input; - PDFPanel fixture; + WebClient client; + + static MockWebServer mockServer; + + @BeforeAll + static void setUpClass() throws IOException { + mockServer = new MockWebServer(); + mockServer.start(); + } + + @AfterAll + static void tearDownClass() throws IOException { + mockServer.shutdown(); + } @BeforeEach void setup() throws FontFormatException, IOException { @@ -38,10 +51,13 @@ void setup() throws FontFormatException, IOException { // Create PDFPanel object to test fixture = new PDFPanel(input, "-1"); + + String baseUrl = String.format("http://localhost:%s", mockServer.getPort()); + client = WebClient.create(baseUrl); } @Test - void testBinaryPathOneFile() throws ApiException { + void testBinaryPathOneFile() throws WebClientResponseException { SCorpusGraph corpusGraph = SampleGenerator.createCorpusStructure(); SDocument doc = corpusGraph.getDocuments().get(0); @@ -49,16 +65,15 @@ void testBinaryPathOneFile() throws ApiException { when(input.getContextPath()).thenReturn("/context"); // Make sure the document has an assigned PDF file - CorporaApi api = mock(CorporaApi.class); - when(api.listFiles(anyString(), anyString())) - .thenReturn(Arrays.asList("notapdf.webm", "test.pdf")); + mockServer.enqueue(new MockResponse().setBody("[\"notapdf.webm\", \"test.pdf\"]") + .addHeader("Content-Type", "application/json")); assertEquals("/context/Binary?toplevelCorpusName=rootCorpus&file=test.pdf", - fixture.getBinaryPath(api)); + fixture.getBinaryPath(client)); } @Test - void testBinaryPathNoFile() throws ApiException { + void testBinaryPathNoFile() throws WebClientResponseException { SCorpusGraph corpusGraph = SampleGenerator.createCorpusStructure(); SDocument doc = corpusGraph.getDocuments().get(0); @@ -66,14 +81,14 @@ void testBinaryPathNoFile() throws ApiException { when(input.getContextPath()).thenReturn("/context"); // Make sure the document has an assigned PDF file - CorporaApi api = mock(CorporaApi.class); - when(api.listFiles(anyString(), anyString())).thenReturn(new LinkedList<>()); + mockServer + .enqueue(new MockResponse().setBody("[]").addHeader("Content-Type", "application/json")); - assertEquals("", fixture.getBinaryPath(api)); + assertEquals("", fixture.getBinaryPath(client)); } @Test - void testBinaryPathApiExceptionThrown() throws ApiException + void testBinaryPathApiExceptionThrown() throws WebClientResponseException { SCorpusGraph corpusGraph = SampleGenerator.createCorpusStructure(); SDocument doc = corpusGraph.getDocuments().get(0); @@ -84,11 +99,9 @@ void testBinaryPathApiExceptionThrown() throws ApiException when(input.getUI()).thenReturn(ui); // Make sure the document has an assigned PDF file - CorporaApi api = mock(CorporaApi.class); - when(api.listFiles(anyString(), anyString())) - .thenThrow(new ApiException("Invalid Network Access")); + mockServer.enqueue(new MockResponse().setResponseCode(500).setBody("Invalid Network Access")); - assertEquals("", fixture.getBinaryPath(api)); + assertEquals("", fixture.getBinaryPath(client)); verify(ui).addWindow(any(ExceptionDialog.class)); } diff --git a/src/test/java/org/corpus_tools/annis/gui/visualizers/component/tree/TigerTreeVisualizerTest.java b/src/test/java/org/corpus_tools/annis/gui/visualizers/component/tree/TigerTreeVisualizerTest.java index e57d7c3a40..e90e4946bc 100644 --- a/src/test/java/org/corpus_tools/annis/gui/visualizers/component/tree/TigerTreeVisualizerTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/visualizers/component/tree/TigerTreeVisualizerTest.java @@ -18,12 +18,10 @@ import javax.imageio.ImageIO; import javax.servlet.ServletContext; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.Helper; import org.corpus_tools.annis.gui.UIConfig; -import org.corpus_tools.annis.gui.VisualizationToggle; import org.corpus_tools.annis.gui.objects.Match; +import org.corpus_tools.annis.gui.util.Helper; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; -import org.corpus_tools.annis.gui.visualizers.component.tree.TigerTreeVisualizer; import org.corpus_tools.salt.SALT_TYPE; import org.corpus_tools.salt.SaltFactory; import org.corpus_tools.salt.common.SDocument; @@ -37,7 +35,6 @@ class TigerTreeVisualizerTest { private AnnisUI ui; private VisualizerInput visInput; - private VisualizationToggle visToggle; private VaadinSession session; private ServletContext servletContext; private UIConfig config = new UIConfig(); @@ -52,7 +49,6 @@ void setup() throws FontFormatException, IOException { // Init mocks visInput = mock(VisualizerInput.class); - visToggle = mock(VisualizationToggle.class); ui = mock(AnnisUI.class); session = mock(VaadinSession.class); servletContext = mock(ServletContext.class); diff --git a/src/test/java/org/corpus_tools/annis/gui/visualizers/htmlvis/HTMLVisTest.java b/src/test/java/org/corpus_tools/annis/gui/visualizers/htmlvis/HTMLVisTest.java index f30f7b16f5..446d70f375 100644 --- a/src/test/java/org/corpus_tools/annis/gui/visualizers/htmlvis/HTMLVisTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/visualizers/htmlvis/HTMLVisTest.java @@ -19,41 +19,53 @@ import static org.mockito.Mockito.when; import com.vaadin.server.Page; -import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import org.apache.commons.io.FileUtils; -import org.corpus_tools.annis.ApiException; -import org.corpus_tools.annis.api.CorporaApi; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; import org.corpus_tools.annis.gui.AnnisBaseUI; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; class HTMLVisTest { - private static final Logger log = LoggerFactory.getLogger(HTMLVisTest.class); private HTMLVis fixture; + static MockWebServer mockServer; + WebClient client; + + @BeforeAll + static void setUpClass() throws IOException { + mockServer = new MockWebServer(); + mockServer.start(); + } + + @AfterAll + static void tearDownClass() throws IOException { + mockServer.shutdown(); + } + @BeforeEach public void setup() { fixture = new HTMLVis(); + String baseUrl = String.format("http://localhost:%s", mockServer.getPort()); + client = WebClient.create(baseUrl); } @Test - void testWebFontInjection() throws ApiException, IOException { + void testWebFontInjection() throws WebClientResponseException, IOException { AnnisBaseUI ui = mock(AnnisBaseUI.class); - CorporaApi api = mock(CorporaApi.class); - File jsonFile = File.createTempFile("test", ".fonts.json"); - FileUtils.writeStringToFile(jsonFile, "{\"web-fonts\": [" + "{" + "\"name\": \"FancyFont\", " - + "\"sources\": {\"format\":\"url\"}}" + "]}", StandardCharsets.UTF_8); - when(api.getFile("rootCorpus", "rootCorpus/test.fonts.json")).thenReturn(jsonFile); + mockServer.enqueue(new MockResponse().setBody("{\"web-fonts\": [" + "{" + "\"name\": \"FancyFont\", " + + "\"sources\": {\"format\":\"url\"}}" + "]}") + .addHeader("Content-Type", "application/json")); - fixture.injectWebFonts("test", "rootCorpus", "rootCorpus", ui, api); + fixture.injectWebFonts("test", "rootCorpus", "rootCorpus", ui, client); verify(ui).injectUniqueCSS( "@font-face {\n" + " font-family: 'FancyFont';\n" + " font-weight: '400';\n" @@ -61,27 +73,25 @@ void testWebFontInjection() throws ApiException, IOException { } @Test - void testWebFontInjectionErrorHandling() throws ApiException, IOException { + void testWebFontInjectionErrorHandling() throws WebClientResponseException, IOException { AnnisBaseUI ui = mock(AnnisBaseUI.class); Page page = mock(Page.class); - CorporaApi api = mock(CorporaApi.class); when(page.getUI()).thenReturn(ui); when(ui.getPage()).thenReturn(page); - // Create invalid JSON file - File jsonFile = File.createTempFile("test", ".fonts.json"); - FileUtils.writeStringToFile(jsonFile, "{\"web-fonts\"}", StandardCharsets.UTF_8); - when(api.getFile("rootCorpus", "rootCorpus/test.fonts.json")).thenReturn(jsonFile); + mockServer.enqueue(new MockResponse().setBody("{\"web-fonts\"}") + .addHeader("Content-Type", "application/json")); + - fixture.injectWebFonts("test", "rootCorpus", "rootCorpus", ui, api); + fixture.injectWebFonts("test", "rootCorpus", "rootCorpus", ui, client); verify(ui, Mockito.never()).injectUniqueCSS(any()); // Don't return a file from the API - when(api.getFile(any(), any())).thenThrow(new ApiException(500, "Server error")); + mockServer.enqueue(new MockResponse().setBody("Server error").setResponseCode(500)); - fixture.injectWebFonts("test", "rootCorpus", "rootCorpus", ui, api); + fixture.injectWebFonts("test", "rootCorpus", "rootCorpus", ui, client); verify(ui, Mockito.never()).injectUniqueCSS(any()); } diff --git a/src/test/java/org/corpus_tools/annis/gui/visualizers/iframe/AbstractIFrameVisualizerTest.java b/src/test/java/org/corpus_tools/annis/gui/visualizers/iframe/AbstractIFrameVisualizerTest.java index 76e873d675..9e6888aa9e 100644 --- a/src/test/java/org/corpus_tools/annis/gui/visualizers/iframe/AbstractIFrameVisualizerTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/visualizers/iframe/AbstractIFrameVisualizerTest.java @@ -14,10 +14,9 @@ import java.util.Optional; import javax.servlet.ServletContext; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.VisualizationToggle; +import org.corpus_tools.annis.gui.resultview.VisualizerPanel; import org.corpus_tools.annis.gui.visualizers.IFrameResourceMap; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; -import org.corpus_tools.annis.gui.visualizers.iframe.AbstractIFrameVisualizer; import org.corpus_tools.annis.gui.widgets.AutoHeightIFrame; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,7 +29,7 @@ class AbstractIFrameVisualizerTest { private VisualizerInput visInput; - private VisualizationToggle visToggle; + private VisualizerPanel visPanel; private AnnisUI ui; @@ -63,7 +62,7 @@ public void writeOutput(VisualizerInput input, OutputStream outstream) { // Init mocks visInput = mock(VisualizerInput.class); - visToggle = mock(VisualizationToggle.class); + visPanel = mock(VisualizerPanel.class); ui = mock(AnnisUI.class); session = mock(VaadinSession.class); servletContext = mock(ServletContext.class); @@ -81,7 +80,7 @@ void contextPathSet() { // Test with trailing slash when(servletContext.getContextPath()).thenReturn("/somestring/"); - Component component = vis.createComponent(visInput, visToggle); + Component component = vis.createComponent(visInput, visPanel); assertNotNull(component); assertTrue(component instanceof AutoHeightIFrame); if (component instanceof AutoHeightIFrame) { @@ -94,7 +93,7 @@ void contextPathSet() { // Test without trailing slash when(servletContext.getContextPath()).thenReturn("/somestring"); - component = vis.createComponent(visInput, visToggle); + component = vis.createComponent(visInput, visPanel); assertNotNull(component); assertTrue(component instanceof AutoHeightIFrame); if (component instanceof AutoHeightIFrame) { diff --git a/src/test/java/org/corpus_tools/annis/gui/visualizers/iframe/CorefVisualizerTest.java b/src/test/java/org/corpus_tools/annis/gui/visualizers/iframe/CorefVisualizerTest.java index f0d8257ebb..02df57bccd 100644 --- a/src/test/java/org/corpus_tools/annis/gui/visualizers/iframe/CorefVisualizerTest.java +++ b/src/test/java/org/corpus_tools/annis/gui/visualizers/iframe/CorefVisualizerTest.java @@ -11,9 +11,7 @@ import java.util.List; import javax.servlet.ServletContext; import org.corpus_tools.annis.gui.AnnisUI; -import org.corpus_tools.annis.gui.VisualizationToggle; import org.corpus_tools.annis.gui.visualizers.VisualizerInput; -import org.corpus_tools.annis.gui.visualizers.iframe.CorefVisualizer; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.common.SDocumentGraph; import org.corpus_tools.salt.common.SToken; @@ -31,7 +29,6 @@ class CorefVisualizerTest { private AnnisUI ui; private VisualizerInput visInput; - private VisualizationToggle visToggle; private VaadinSession session; private ServletContext servletContext; @@ -42,7 +39,6 @@ void setup() { // Init mocks visInput = mock(VisualizerInput.class); - visToggle = mock(VisualizationToggle.class); ui = mock(AnnisUI.class); session = mock(VaadinSession.class); servletContext = mock(ServletContext.class);