From 30d4f0ad09022f163623bce619f544f870dad9c5 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 11 Aug 2017 10:41:21 +0200 Subject: [PATCH] Improve missing imports resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Possibility to resolve missing imports from XML Catalog and not only an ontology file. Possibility to change the active catalog. Fix: the classes hierarchy was not reloaded when adding “Direct Imports” in “Imported Ontologies” window --- .../model/library/OntologyCatalogManager.java | 8 + .../org/protege/editor/owl/ui/UIHelper.java | 8 + .../ChangeActiveOntologyLibraryAction.java | 65 +++++++ .../owl/ui/library/XMLCatalogManager.java | 179 ++++++++++++++++++ .../ontology/imports/AddImportsStrategy.java | 75 ++++---- .../missing/MissingImportHandlerUI.java | 41 +++- .../src/main/resources/plugin.xml | 11 +- 7 files changed, 338 insertions(+), 49 deletions(-) create mode 100644 protege-editor-owl/src/main/java/org/protege/editor/owl/ui/library/ChangeActiveOntologyLibraryAction.java create mode 100644 protege-editor-owl/src/main/java/org/protege/editor/owl/ui/library/XMLCatalogManager.java diff --git a/protege-editor-owl/src/main/java/org/protege/editor/owl/model/library/OntologyCatalogManager.java b/protege-editor-owl/src/main/java/org/protege/editor/owl/model/library/OntologyCatalogManager.java index fcde0ee76..08a439e4c 100644 --- a/protege-editor-owl/src/main/java/org/protege/editor/owl/model/library/OntologyCatalogManager.java +++ b/protege-editor-owl/src/main/java/org/protege/editor/owl/model/library/OntologyCatalogManager.java @@ -7,8 +7,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; import java.net.URI; import java.util.*; @@ -175,6 +177,12 @@ public List getAllCatalogs() { public XMLCatalog getActiveCatalog() { return activeCatalog; } + + public void changeActiveCatalog(@Nonnull File newCatalogFile) throws IOException { + File newCatalogDir = newCatalogFile.getParentFile(); + localCatalogs.put(newCatalogDir, CatalogUtilities.parseDocument(newCatalogFile.toURI().toURL())); + activeCatalog = localCatalogs.get(newCatalogDir); + } public File getActiveCatalogFolder() { return activeCatalogFolder; diff --git a/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/UIHelper.java b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/UIHelper.java index 59e8a5bbe..a4ec13ea3 100644 --- a/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/UIHelper.java +++ b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/UIHelper.java @@ -263,6 +263,14 @@ public File chooseOWLFile(String title) { return UIUtil.openFile(f, title, "OWL File", OWL_EXTENSIONS); } + public File chooseOWLOrCatalogFile(String title) { + JFrame f = (JFrame) SwingUtilities.getAncestorOfClass(JFrame.class, getParent()); + if (f == null) { + f = new JFrame(); + } + return UIUtil.openFile(f, title, "OWL or Catalog File", OWL_EXTENSIONS); + } + public File saveOWLFile(String title) { return UIUtil.saveFile((JFrame) SwingUtilities.getAncestorOfClass(JFrame.class, getParent()), diff --git a/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/library/ChangeActiveOntologyLibraryAction.java b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/library/ChangeActiveOntologyLibraryAction.java new file mode 100644 index 000000000..0b9537f7a --- /dev/null +++ b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/library/ChangeActiveOntologyLibraryAction.java @@ -0,0 +1,65 @@ +package org.protege.editor.owl.ui.library; + +import org.protege.editor.core.ProtegeManager; +import org.protege.editor.core.ui.util.UIUtil; +import org.protege.editor.core.ui.workspace.WorkspaceFrame; +import org.protege.editor.owl.model.library.OntologyCatalogManager; +import org.protege.editor.owl.ui.action.ProtegeOWLAction; +import org.protege.editor.owl.ui.ontology.imports.AddImportsStrategy; +import org.protege.editor.owl.ui.ontology.imports.wizard.GetImportsVisitor; +import org.protege.editor.owl.ui.ontology.imports.wizard.ImportInfo; +import org.protege.xmlcatalog.XMLCatalog; +import org.protege.xmlcatalog.entry.Entry; +import org.semanticweb.owlapi.model.OWLOntology; +import org.slf4j.LoggerFactory; + +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Created by vblagodarov on 24-07-17. + */ +public class ChangeActiveOntologyLibraryAction extends ProtegeOWLAction { + public void actionPerformed(ActionEvent e) { + try { + WorkspaceFrame frame = ProtegeManager.getInstance().getFrame(getOWLWorkspace()); + File catalogFile = UIUtil.openFile( + frame, + "Choose catalog file containing ontology repository information", + "Choose XML Catalog", + Collections.singleton("xml")); + if (catalogFile != null) { + OntologyCatalogManager catalogManager = getOWLModelManager().getOntologyCatalogManager(); + catalogManager.changeActiveCatalog(catalogFile); + XMLCatalog activeCatalog = catalogManager.getActiveCatalog(); + GetImportsVisitor getter = new GetImportsVisitor(); + for (Entry entry : activeCatalog.getEntries()) { + entry.accept(getter); + } + OWLOntology activeOntology = getOWLModelManager().getActiveOntology(); + XMLCatalogManager xmlCatalogManager = new XMLCatalogManager(activeCatalog); + Set importsToImport = xmlCatalogManager.getImports().stream().filter( + importInfo -> activeOntology.getDirectImportsDocuments().contains(importInfo.getImportLocation())).collect(Collectors.toSet()); + + AddImportsStrategy ais = new AddImportsStrategy(getOWLEditorKit(), activeOntology, importsToImport); + ais.addImports(); + } + } catch (Exception ex) { + LoggerFactory.getLogger(EditActiveOntologyLibraryAction.class) + .error("An error occurred whilst attempting to change ontology catalog: {}", e); + } + } + + @Override + public void initialise() throws Exception { + + } + + @Override + public void dispose() throws Exception { + + } +} diff --git a/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/library/XMLCatalogManager.java b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/library/XMLCatalogManager.java new file mode 100644 index 000000000..590159465 --- /dev/null +++ b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/library/XMLCatalogManager.java @@ -0,0 +1,179 @@ +package org.protege.editor.owl.ui.library; + +import com.google.common.base.Optional; +import org.protege.editor.owl.model.repository.MasterOntologyIDExtractor; +import org.protege.editor.owl.ui.ontology.imports.wizard.GetImportsVisitor; +import org.protege.editor.owl.ui.ontology.imports.wizard.ImportInfo; +import org.protege.xmlcatalog.EntryVisitor; +import org.protege.xmlcatalog.XMLCatalog; +import org.protege.xmlcatalog.entry.*; +import org.protege.xmlcatalog.owlapi.XMLCatalogIRIMapper; +import org.semanticweb.owlapi.model.IRI; +import org.semanticweb.owlapi.model.OWLOntologyID; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.net.URI; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Created by vblagodarov on 01-08-17. + */ +public class XMLCatalogManager { + + XMLCatalogIRIMapper iriMapper; + XMLCatalogEntriesVisitor entriesVisitor; + MasterOntologyIDExtractor extractor = new MasterOntologyIDExtractor(); + XMLCatalog catalog; + + public XMLCatalogManager(XMLCatalog xmlCatalog) { + catalog = xmlCatalog; + iriMapper = new XMLCatalogIRIMapper(xmlCatalog); + entriesVisitor = new XMLCatalogEntriesVisitor(); + for (Entry entry : xmlCatalog.getEntries()) { + entry.accept(entriesVisitor); + } + } + + public XMLCatalog getCatalog() { + return catalog; + } + + public Collection getImports() { + Set imports = new HashSet<>(); + for (XMLCatalogUriEntry entry : getAllUriEntries()) { + ImportInfo myImport = new ImportInfo(); + myImport.setImportLocation(IRI.create(entry.getEntry().getName())); + myImport.setPhysicalLocation(entry.getPhysicalLocation()); + + Optional id = extractor.getOntologyId(entry.getPhysicalLocation()); + //If ID is not set, the import will be ignored + if (id.isPresent()) { + myImport.setOntologyID(id.get()); + } + imports.add(myImport); + } + return imports; + } + + public boolean containsUri(URI physicalLocation) { + if (physicalLocation == null) { + return false; + } + return getAllUriEntries().stream().filter( + entry -> entry.getPhysicalLocation().equals(physicalLocation)).findAny().isPresent(); + } + + public Collection getAllUriEntries() { + Set entries = new HashSet<>(); + for (UriEntry uriEntry : entriesVisitor.getAllUriEntries()) { + IRI ontologyIRI = IRI.create(uriEntry.getName()); + URI redirect = iriMapper.getDocumentIRI(ontologyIRI).toURI(); + entries.add(redirect == null ? new XMLCatalogUriEntry(ontologyIRI, uriEntry) : + new XMLCatalogUriEntry(ontologyIRI, uriEntry, redirect)); + } + + return entries; + } + + public class XMLCatalogUriEntry { + private UriEntry entry; + private URI location; + private IRI ontologyIRI; + + public XMLCatalogUriEntry(@Nonnull IRI ontologyIRI, @Nonnull UriEntry uriEntry) { + this(ontologyIRI, uriEntry, uriEntry.getAbsoluteURI()); + } + + public XMLCatalogUriEntry(@Nonnull IRI ontologyIRI, @Nonnull UriEntry uriEntry, @Nonnull URI physicalLocation) { + this.ontologyIRI = ontologyIRI; + entry = uriEntry; + location = physicalLocation; + } + + public UriEntry getEntry() { + return entry; + } + + public IRI getOntologyIRI() { + return ontologyIRI; + } + + public URI getPhysicalLocation() { + return location; + } + } + + private class XMLCatalogEntriesVisitor implements EntryVisitor { + + private Set uriEntries = new HashSet<>(); + + public Set getAllUriEntries() { + return uriEntries; + } + + @Override + public void visit(UriEntry entry) { + uriEntries.add(entry); + } + + @Override + public void visit(GroupEntry entry) { + for (Entry subEntry : entry.getEntries()) { + subEntry.accept(this); + } + } + + @Override + public void visit(PublicEntry entry) { + + } + + @Override + public void visit(SystemEntry entry) { + + } + + @Override + public void visit(RewriteSystemEntry entry) { + + } + + @Override + public void visit(DelegatePublicEntry entry) { + + } + + @Override + public void visit(DelegateSystemEntry entry) { + + } + + @Override + public void visit(RewriteUriEntry entry) { + + } + + @Override + public void visit(DelegateUriEntry entry) { + + } + + @Override + public void visit(NextCatalogEntry entry) { + + try { + XMLCatalog catalog = entry.getParsedCatalog(); + for (Entry subEntry : catalog.getEntries()) { + subEntry.accept(this); + } + } catch (IOException e) { + LoggerFactory.getLogger(GetImportsVisitor.class) + .error("Ad error occurred whilst attempting to process the XMLCatalog file: {}", e); + } + } + } +} diff --git a/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/ontology/imports/AddImportsStrategy.java b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/ontology/imports/AddImportsStrategy.java index 2f3c2cbff..c5c6296d6 100644 --- a/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/ontology/imports/AddImportsStrategy.java +++ b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/ontology/imports/AddImportsStrategy.java @@ -6,6 +6,8 @@ import org.protege.editor.owl.OWLEditorKit; import org.protege.editor.owl.model.io.*; import org.protege.editor.owl.model.library.OntologyCatalogManager; +import org.protege.editor.owl.model.selection.ontologies.ImportsClosureOntologySelectionStrategy; +import org.protege.editor.owl.ui.library.XMLCatalogManager; import org.protege.editor.owl.ui.ontology.imports.wizard.ImportInfo; import org.protege.editor.owl.ui.util.ProgressDialog; import org.protege.xmlcatalog.CatalogUtilities; @@ -15,7 +17,6 @@ import org.semanticweb.owlapi.io.IRIDocumentSource; import org.semanticweb.owlapi.model.*; import org.semanticweb.owlapi.model.parameters.OntologyCopy; -import org.semanticweb.owlapi.util.SimpleIRIMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,9 +24,7 @@ import java.io.File; import java.io.IOException; import java.net.URI; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.util.*; import java.util.concurrent.Executors; /** @@ -64,6 +63,7 @@ private void addImportsInOtherThread() { SwingUtilities.invokeLater(() -> { logger.info("Adding imports statements"); editorKit.getModelManager().applyChanges(result); + editorKit.getOWLModelManager().setActiveOntologiesStrategy(new ImportsClosureOntologySelectionStrategy(editorKit.getOWLModelManager())); logger.info("Finished adding imports"); }); return result; @@ -74,20 +74,38 @@ private void addImportsInOtherThread() { dlg.setVisible(true); } - private List loadImportsInternal() { + OntologyCatalogManager catalogManager = editorKit.getOWLModelManager().getOntologyCatalogManager(); + XMLCatalogManager xmlCatalogManager = null; + OWLOntology ontology = editorKit.getModelManager().getActiveOntology(); + IRI importersDocumentLocation = ontology.getOWLOntologyManager().getOntologyDocumentIRI(ontology); + File f = null; + XMLCatalog catalog = null; + boolean isLocalFile=false; + if (UIUtil.isLocalFile(importersDocumentLocation.toURI())) { + f = new File(importersDocumentLocation.toURI()); + catalog = editorKit.getModelManager().addRootFolder(f.getParentFile()); + xmlCatalogManager = new XMLCatalogManager(catalogManager.getActiveCatalog()); + isLocalFile = true; + } + List changes = new ArrayList<>(); for (ImportInfo importParameters : importInfo) { logger.info(LogBanner.start("Importing ontology and imports closure")); logger.info("Processing {}", importParameters.getImportsDeclarationIRI()); dlg.setMessage("Importing " + importParameters.getImportsDeclarationIRI()); IRI importedOntologyDocumentIRI = importParameters.getImportsDeclarationIRI(); - URI physicalLocation = importParameters.getPhysicalLocation(); - OntologyCatalogManager catalogManager = editorKit.getOWLModelManager().getOntologyCatalogManager(); - if (willRedirectTotheWrongPlace(catalogManager, importedOntologyDocumentIRI, physicalLocation)) { - OWLOntology activeOntology = editorKit.getModelManager().getActiveOntology(); - addImportMapping(activeOntology, importedOntologyDocumentIRI, IRI.create(physicalLocation)); + if(isLocalFile){ + URI physicalLocationURI = CatalogUtilities.relativize(importParameters.getPhysicalLocation(), catalog); + if(!xmlCatalogManager.containsUri(physicalLocationURI)){ + catalog.addEntry(0, new UriEntry("Imports Wizard Entry", catalog, importedOntologyDocumentIRI.toURI().toString(), physicalLocationURI, null)); + } + try { + CatalogUtilities.save(catalog, OntologyCatalogManager.getCatalogFile(f.getParentFile())); + } catch (IOException e) { + logger.warn("An error occurred whilst saving the catalog file: {}", e); + } } OWLOntologyManager man = editorKit.getOWLModelManager().getOWLOntologyManager(); @@ -95,15 +113,13 @@ private List loadImportsInternal() { if (!man.contains(importParameters.getOntologyID())) { try { OWLOntologyManager loadingManager = OWLManager.createConcurrentOWLOntologyManager(); - loadingManager.getIRIMappers() - .add(man.getIRIMappers()); + loadingManager.getIRIMappers().add(man.getIRIMappers()); ProgressDialogOntologyLoaderListener listener = new ProgressDialogOntologyLoaderListener(dlg, logger); loadingManager.addOntologyLoaderListener(listener); loadingManager.loadOntologyFromOntologyDocument( - new IRIDocumentSource(IRI.create(physicalLocation)), + new IRIDocumentSource(IRI.create(importParameters.getPhysicalLocation())), new OWLOntologyLoaderConfiguration().setMissingImportHandlingStrategy(MissingImportHandlingStrategy.SILENT)); loadingManager.removeOntologyLoaderListener(listener); -// editorKit.getModelManager().fireEvent(EventType.ONTOLOGY_LOADED); for(OWLOntology importedOntology : loadingManager.getOntologies()) { IRI ontologyDocumentIRI = loadingManager.getOntologyDocumentIRI(importedOntology); if (!man.contains(importedOntology.getOntologyID())) { @@ -125,6 +141,8 @@ private List loadImportsInternal() { } catch (OWLOntologyCreationException e) { logger.error("There was a problem loading the ontology from {}. Error: {}", importedOntologyDocumentIRI, e.getMessage(), e); JOptionPane.showMessageDialog(editorKit.getOWLWorkspace(), "An error occurred whilst the ontology at " + importedOntologyDocumentIRI + " was being loaded.", "Error loading ontology", JOptionPane.ERROR_MESSAGE); + } catch (Exception e) { + e.printStackTrace(); } } changes.add(new AddImport(toOntology, decl)); @@ -132,33 +150,4 @@ private List loadImportsInternal() { } return changes; } - - private static boolean willRedirectTotheWrongPlace(OntologyCatalogManager catalogManager, IRI importLocation, URI physicalLocation) { - if (catalogManager.getRedirect(importLocation.toURI()) == null) { - return !importLocation.equals(IRI.create(physicalLocation)); - } - else { - return !physicalLocation.equals(catalogManager.getRedirect(importLocation.toURI())); - } - } - - private void addImportMapping(OWLOntology ontology, IRI importLocation, IRI physicalLocation) { - OWLOntologyManager manager = ontology.getOWLOntologyManager(); - - manager.getIRIMappers().add(new SimpleIRIMapper(importLocation, physicalLocation)); - IRI importersDocumentLocation = manager.getOntologyDocumentIRI(ontology); - if (UIUtil.isLocalFile(importersDocumentLocation.toURI())) { - File f = new File(importersDocumentLocation.toURI()); - XMLCatalog catalog = editorKit.getModelManager().addRootFolder(f.getParentFile()); - URI physicalUri = CatalogUtilities.relativize(physicalLocation.toURI(), catalog); - catalog.addEntry(0, new UriEntry("Imports Wizard Entry", catalog, importLocation.toURI().toString(), physicalUri, null)); - try { - CatalogUtilities.save(catalog, OntologyCatalogManager.getCatalogFile(f.getParentFile())); - } catch (IOException e) { - logger.warn("An error occurred whilst saving the catalog file: {}", e); - } - } - } - - } diff --git a/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/ontology/imports/missing/MissingImportHandlerUI.java b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/ontology/imports/missing/MissingImportHandlerUI.java index 95d929b14..7d923daaa 100644 --- a/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/ontology/imports/missing/MissingImportHandlerUI.java +++ b/protege-editor-owl/src/main/java/org/protege/editor/owl/ui/ontology/imports/missing/MissingImportHandlerUI.java @@ -4,8 +4,10 @@ import org.protege.editor.owl.model.MissingImportHandler; import org.protege.editor.owl.model.library.OntologyCatalogManager; import org.protege.editor.owl.ui.UIHelper; +import org.protege.editor.owl.ui.library.XMLCatalogManager; import org.protege.xmlcatalog.CatalogUtilities; import org.protege.xmlcatalog.XMLCatalog; +import org.protege.xmlcatalog.entry.NextCatalogEntry; import org.protege.xmlcatalog.entry.UriEntry; import org.semanticweb.owlapi.model.IRI; import org.slf4j.Logger; @@ -15,6 +17,9 @@ import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; @@ -33,6 +38,7 @@ public class MissingImportHandlerUI implements MissingImportHandler { private final Logger logger = LoggerFactory.getLogger(MissingImportHandlerUI.class); private final OWLEditorKit owlEditorKit; + private Map cache = new HashMap<>(); public MissingImportHandlerUI(OWLEditorKit owlEditorKit) { @@ -41,6 +47,10 @@ public MissingImportHandlerUI(OWLEditorKit owlEditorKit) { public IRI getDocumentIRI(IRI ontologyIRI) { + IRI fromCache = cache.get(ontologyIRI); + if(fromCache !=null){ + return fromCache; + } FutureTask futureTask = new FutureTask<>(() -> { int ret = JOptionPane.showConfirmDialog(null, "The system couldn't locate the ontology:
" + ontologyIRI.toString() + "

" + @@ -53,12 +63,27 @@ public IRI getDocumentIRI(IRI ontologyIRI) { return null; } UIHelper helper = new UIHelper(owlEditorKit); - File file = helper.chooseOWLFile("Please select an ontology file"); + File file = helper.chooseOWLOrCatalogFile("Please select an ontology or a catalog file"); if (file == null) { return ontologyIRI; } - updateActiveCatalog(ontologyIRI, file); - return IRI.create(file); + boolean isFileCatalog = true; + XMLCatalogManager xmlCatalogManager=null; + try{ + XMLCatalog catalog = CatalogUtilities.parseDocument(file.toURI().toURL()); + xmlCatalogManager = new XMLCatalogManager(catalog); + } + catch (Exception e){ + ;//Not a catalog, probably an ontology + isFileCatalog = false; + updateActiveCatalog(ontologyIRI, file, isFileCatalog); + return IRI.create(file); + } + + updateActiveCatalog(ontologyIRI, file, isFileCatalog); + xmlCatalogManager.getAllUriEntries().forEach(entry -> cache.put(entry.getOntologyIRI(), IRI.create(entry.getPhysicalLocation()))); + IRI result =cache.get(ontologyIRI); + return result == null ? ontologyIRI: result; }); SwingUtilities.invokeLater(futureTask); @@ -73,14 +98,20 @@ public IRI getDocumentIRI(IRI ontologyIRI) { } } - private void updateActiveCatalog(IRI ontologyIRI, File file) { + private void updateActiveCatalog(IRI ontologyIRI, File file, boolean isFileCatalog) { OntologyCatalogManager catalogManager = owlEditorKit.getOWLModelManager().getOntologyCatalogManager(); XMLCatalog activeCatalog = catalogManager.getActiveCatalog(); if (activeCatalog == null) { return; } URI relativeFile = CatalogUtilities.relativize(file.toURI(), activeCatalog); - activeCatalog.addEntry(0, new UriEntry("User Entered Import Resolution", activeCatalog, ontologyIRI.toString(), relativeFile, null)); + if(isFileCatalog){ + activeCatalog.addEntry(0, new NextCatalogEntry("User Entered Import Resolution", activeCatalog, relativeFile, null)); + } + else{ + activeCatalog.addEntry(0, new UriEntry("User Entered Import Resolution", activeCatalog, ontologyIRI.toString(), relativeFile, null)); + } + File catalogLocation = new File(activeCatalog.getXmlBaseContext().getXmlBase()); try { CatalogUtilities.save(activeCatalog, catalogLocation); diff --git a/protege-editor-owl/src/main/resources/plugin.xml b/protege-editor-owl/src/main/resources/plugin.xml index 7dd7dfbe6..52d1acc72 100644 --- a/protege-editor-owl/src/main/resources/plugin.xml +++ b/protege-editor-owl/src/main/resources/plugin.xml @@ -1160,12 +1160,21 @@ + + + + + + + + - +