From c700d7ca4b5c22e2bab864256580dee18878a2bf Mon Sep 17 00:00:00 2001 From: Admir Obralija Date: Tue, 9 Aug 2016 14:14:01 +0200 Subject: [PATCH] Implementation of shared database support (full system). (#1451) Support for MySQL, PostgreSQL and Oracle database systems. Full entry synchronisation. Full meta data synchronization. Group synchrnoization. Semi-automatic/event based synchronization. Version control system. Conflicts resolving machanisms. User interfaces for different situations. Extensive synchronization and class tests. --- .gitignore | 3 + .travis.yml | 1 + CHANGELOG.md | 1 + .../net/sf/jabref/BibDatabaseContext.java | 38 ++ src/main/java/net/sf/jabref/MetaData.java | 75 ++- .../sf/jabref/event/GroupUpdatedEvent.java | 19 + .../sf/jabref/event/MetaDataChangedEvent.java | 22 + .../jabref/event/source/EntryEventSource.java | 26 + .../java/net/sf/jabref/gui/BasePanel.java | 142 +---- .../java/net/sf/jabref/gui/IconTheme.java | 2 +- .../java/net/sf/jabref/gui/JabRefFrame.java | 78 ++- .../net/sf/jabref/gui/actions/Actions.java | 2 +- .../gui/actions/OpenSharedDatabaseAction.java | 32 ++ .../jabref/gui/entryeditor/EntryEditor.java | 1 + .../sf/jabref/gui/groups/GroupSelector.java | 18 +- .../sf/jabref/gui/keyboard/KeyBinding.java | 1 + .../gui/shared/MergeSharedEntryDialog.java | 112 ++++ .../gui/shared/OpenSharedDatabaseDialog.java | 339 ++++++++++++ .../gui/shared/SharedDatabaseUIManager.java | 83 +++ .../jabref/gui/undo/UndoableRemoveEntry.java | 3 +- .../logic/cleanup/FieldFormatterCleanup.java | 3 +- .../logic/exporter/BibDatabaseWriter.java | 5 + .../exporter/FieldFormatterCleanups.java | 2 +- .../net/sf/jabref/logic/util/Version.java | 3 +- .../sf/jabref/model/database/BibDatabase.java | 36 +- .../model/database/DatabaseLocation.java | 11 + .../net/sf/jabref/model/entry/BibEntry.java | 59 +- .../model/entry/SharedBibEntryData.java | 37 ++ .../jabref/model/event/EntryAddedEvent.java | 20 +- .../jabref/model/event/EntryChangedEvent.java | 9 + .../net/sf/jabref/model/event/EntryEvent.java | 18 +- .../jabref/model/event/EntryRemovedEvent.java | 9 + .../jabref/model/event/FieldChangedEvent.java | 27 +- .../jabref/preferences/JabRefPreferences.java | 4 + .../shared/DBMSConnectionProperties.java | 78 +++ .../net/sf/jabref/shared/DBMSConnector.java | 84 +++ .../net/sf/jabref/shared/DBMSProcessor.java | 513 ++++++++++++++++++ .../sf/jabref/shared/DBMSSynchronizer.java | 370 +++++++++++++ .../java/net/sf/jabref/shared/DBMSType.java | 86 +++ .../net/sf/jabref/shared/MySQLProcessor.java | 47 ++ .../net/sf/jabref/shared/OracleProcessor.java | 54 ++ .../sf/jabref/shared/PostgreSQLProcessor.java | 46 ++ .../shared/event/ConnectionLostEvent.java | 22 + .../event/SharedEntryNotPresentEvent.java | 23 + .../shared/event/UpdateRefusedEvent.java | 37 ++ .../exception/OfflineLockException.java | 28 + .../SharedEntryNotPresentException.java | 21 + .../net/sf/jabref/sql/DBConnectDialog.java | 291 ---------- .../sql/DBExporterAndImporterFactory.java | 60 -- .../sf/jabref/sql/DBImportExportDialog.java | 261 --------- .../java/net/sf/jabref/sql/DBStrings.java | 101 ---- .../sf/jabref/sql/DBStringsPreferences.java | 52 -- src/main/java/net/sf/jabref/sql/Database.java | 23 - .../java/net/sf/jabref/sql/DatabaseType.java | 38 -- .../java/net/sf/jabref/sql/DatabaseUtil.java | 107 ---- .../net/sf/jabref/sql/DbConnectAction.java | 104 ---- src/main/java/net/sf/jabref/sql/SQLUtil.java | 237 -------- .../net/sf/jabref/sql/database/MySQL.java | 137 ----- .../sf/jabref/sql/database/PostgreSQL.java | 147 ----- .../jabref/sql/exporter/DatabaseExporter.java | 474 ---------------- .../jabref/sql/importer/DBImporterResult.java | 41 -- .../jabref/sql/importer/DatabaseImporter.java | 308 ----------- .../jabref/sql/importer/DbImportAction.java | 220 -------- src/main/resources/l10n/JabRef_da.properties | 68 +-- src/main/resources/l10n/JabRef_de.properties | 72 +-- src/main/resources/l10n/JabRef_en.properties | 66 +-- src/main/resources/l10n/JabRef_es.properties | 66 ++- src/main/resources/l10n/JabRef_fa.properties | 71 +-- src/main/resources/l10n/JabRef_fr.properties | 68 +-- src/main/resources/l10n/JabRef_in.properties | 68 +-- src/main/resources/l10n/JabRef_it.properties | 68 +-- src/main/resources/l10n/JabRef_ja.properties | 67 ++- src/main/resources/l10n/JabRef_nl.properties | 67 ++- src/main/resources/l10n/JabRef_no.properties | 69 +-- .../resources/l10n/JabRef_pt_BR.properties | 68 +-- src/main/resources/l10n/JabRef_ru.properties | 67 ++- src/main/resources/l10n/JabRef_sv.properties | 70 +-- src/main/resources/l10n/JabRef_tr.properties | 66 ++- src/main/resources/l10n/JabRef_vi.properties | 69 +-- src/main/resources/l10n/JabRef_zh.properties | 66 ++- src/main/resources/l10n/Menu_da.properties | 6 +- src/main/resources/l10n/Menu_de.properties | 6 +- src/main/resources/l10n/Menu_en.properties | 5 +- src/main/resources/l10n/Menu_es.properties | 5 +- src/main/resources/l10n/Menu_fa.properties | 5 +- src/main/resources/l10n/Menu_fr.properties | 5 +- src/main/resources/l10n/Menu_in.properties | 5 +- src/main/resources/l10n/Menu_it.properties | 5 +- src/main/resources/l10n/Menu_ja.properties | 5 +- src/main/resources/l10n/Menu_nl.properties | 6 +- src/main/resources/l10n/Menu_no.properties | 5 +- src/main/resources/l10n/Menu_pt_BR.properties | 5 +- src/main/resources/l10n/Menu_ru.properties | 5 +- src/main/resources/l10n/Menu_sv.properties | 5 +- src/main/resources/l10n/Menu_tr.properties | 5 +- src/main/resources/l10n/Menu_vi.properties | 5 +- src/main/resources/l10n/Menu_zh.properties | 5 +- .../sf/jabref/shared/DBMSConnectorTest.java | 41 ++ .../sf/jabref/shared/DBMSProcessorTest.java | 326 +++++++++++ .../jabref/shared/DBMSSynchronizerTest.java | 232 ++++++++ .../net/sf/jabref/shared/DBMSTypeTest.java | 44 ++ .../SynchronizationTestEventListener.java | 31 ++ .../shared/SynchronizationTestSimulator.java | 181 ++++++ .../net/sf/jabref/shared/TestConnector.java | 35 ++ .../jabref/sql/DatabaseImportExportTests.java | 189 ------- 105 files changed, 3999 insertions(+), 3575 deletions(-) create mode 100644 src/main/java/net/sf/jabref/event/GroupUpdatedEvent.java create mode 100644 src/main/java/net/sf/jabref/event/MetaDataChangedEvent.java create mode 100644 src/main/java/net/sf/jabref/event/source/EntryEventSource.java create mode 100644 src/main/java/net/sf/jabref/gui/actions/OpenSharedDatabaseAction.java create mode 100644 src/main/java/net/sf/jabref/gui/shared/MergeSharedEntryDialog.java create mode 100644 src/main/java/net/sf/jabref/gui/shared/OpenSharedDatabaseDialog.java create mode 100644 src/main/java/net/sf/jabref/gui/shared/SharedDatabaseUIManager.java create mode 100644 src/main/java/net/sf/jabref/model/database/DatabaseLocation.java create mode 100644 src/main/java/net/sf/jabref/model/entry/SharedBibEntryData.java create mode 100644 src/main/java/net/sf/jabref/shared/DBMSConnectionProperties.java create mode 100644 src/main/java/net/sf/jabref/shared/DBMSConnector.java create mode 100644 src/main/java/net/sf/jabref/shared/DBMSProcessor.java create mode 100644 src/main/java/net/sf/jabref/shared/DBMSSynchronizer.java create mode 100644 src/main/java/net/sf/jabref/shared/DBMSType.java create mode 100644 src/main/java/net/sf/jabref/shared/MySQLProcessor.java create mode 100644 src/main/java/net/sf/jabref/shared/OracleProcessor.java create mode 100644 src/main/java/net/sf/jabref/shared/PostgreSQLProcessor.java create mode 100644 src/main/java/net/sf/jabref/shared/event/ConnectionLostEvent.java create mode 100644 src/main/java/net/sf/jabref/shared/event/SharedEntryNotPresentEvent.java create mode 100644 src/main/java/net/sf/jabref/shared/event/UpdateRefusedEvent.java create mode 100644 src/main/java/net/sf/jabref/shared/exception/OfflineLockException.java create mode 100644 src/main/java/net/sf/jabref/shared/exception/SharedEntryNotPresentException.java delete mode 100644 src/main/java/net/sf/jabref/sql/DBConnectDialog.java delete mode 100644 src/main/java/net/sf/jabref/sql/DBExporterAndImporterFactory.java delete mode 100644 src/main/java/net/sf/jabref/sql/DBImportExportDialog.java delete mode 100644 src/main/java/net/sf/jabref/sql/DBStrings.java delete mode 100644 src/main/java/net/sf/jabref/sql/DBStringsPreferences.java delete mode 100644 src/main/java/net/sf/jabref/sql/Database.java delete mode 100644 src/main/java/net/sf/jabref/sql/DatabaseType.java delete mode 100644 src/main/java/net/sf/jabref/sql/DatabaseUtil.java delete mode 100644 src/main/java/net/sf/jabref/sql/DbConnectAction.java delete mode 100644 src/main/java/net/sf/jabref/sql/SQLUtil.java delete mode 100644 src/main/java/net/sf/jabref/sql/database/MySQL.java delete mode 100644 src/main/java/net/sf/jabref/sql/database/PostgreSQL.java delete mode 100644 src/main/java/net/sf/jabref/sql/exporter/DatabaseExporter.java delete mode 100644 src/main/java/net/sf/jabref/sql/importer/DBImporterResult.java delete mode 100644 src/main/java/net/sf/jabref/sql/importer/DatabaseImporter.java delete mode 100644 src/main/java/net/sf/jabref/sql/importer/DbImportAction.java create mode 100644 src/test/java/net/sf/jabref/shared/DBMSConnectorTest.java create mode 100644 src/test/java/net/sf/jabref/shared/DBMSProcessorTest.java create mode 100644 src/test/java/net/sf/jabref/shared/DBMSSynchronizerTest.java create mode 100644 src/test/java/net/sf/jabref/shared/DBMSTypeTest.java create mode 100644 src/test/java/net/sf/jabref/shared/SynchronizationTestEventListener.java create mode 100644 src/test/java/net/sf/jabref/shared/SynchronizationTestSimulator.java create mode 100644 src/test/java/net/sf/jabref/shared/TestConnector.java delete mode 100644 src/test/java/net/sf/jabref/sql/DatabaseImportExportTests.java diff --git a/.gitignore b/.gitignore index 68d80273720c..b36c5968f661 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # no generated files in version control src/main/gen/ +# do not distribute Oracle's JDBC driver +lib/ojdbc.jar + # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err* diff --git a/.travis.yml b/.travis.yml index bb3969b6eb5b..762cd8a37627 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ install: true # allow gui testing on travis before_script: - psql -c 'create database jabref;' -U postgres + - mysql -u root -e 'create database jabref' script: - ./gradlew check diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e0f66405887..8b7fa04d4400 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# ## [Unreleased] ### Changed +- [#970](https://github.com/JabRef/jabref/issues/970): Implementation of shared database support (full system) with event based synchronization for MySQL, PostgreSQL and Oracle database systems. - Externally fetched information can be merged for entries with an ISBN - Externally fetched information can be merged for entries with an ArXiv eprint - [#462](https://github.com/JabRef/jabref/issues/462) Extend the OpenConsoleFeature by offering a selection between default terminal emulator and configurable command execution. diff --git a/src/main/java/net/sf/jabref/BibDatabaseContext.java b/src/main/java/net/sf/jabref/BibDatabaseContext.java index 3d590d2e8861..dd0d805f0c95 100644 --- a/src/main/java/net/sf/jabref/BibDatabaseContext.java +++ b/src/main/java/net/sf/jabref/BibDatabaseContext.java @@ -10,8 +10,10 @@ import net.sf.jabref.model.database.BibDatabase; import net.sf.jabref.model.database.BibDatabaseMode; import net.sf.jabref.model.database.BibDatabaseModeDetection; +import net.sf.jabref.model.database.DatabaseLocation; import net.sf.jabref.model.entry.FieldName; import net.sf.jabref.preferences.JabRefPreferences; +import net.sf.jabref.shared.DBMSSynchronizer; /** * Represents everything related to a BIB file. @@ -25,6 +27,8 @@ public class BibDatabaseContext { private final Defaults defaults; /** The file where this database was last saved to. */ private File file; + private DBMSSynchronizer dbmsSynchronizer; + private DatabaseLocation location; public BibDatabaseContext() { this(new Defaults()); @@ -43,9 +47,15 @@ public BibDatabaseContext(BibDatabase database, Defaults defaults) { } public BibDatabaseContext(BibDatabase database, MetaData metaData, Defaults defaults) { + this(database, metaData, defaults, DatabaseLocation.LOCAL); + } + + public BibDatabaseContext(BibDatabase database, MetaData metaData, Defaults defaults, DatabaseLocation location) { this.defaults = Objects.requireNonNull(defaults); this.database = Objects.requireNonNull(database); this.metaData = Objects.requireNonNull(metaData); + + updateDatabaseLocation(location); } public BibDatabaseContext(BibDatabase database, MetaData metaData) { @@ -62,6 +72,10 @@ public BibDatabaseContext(BibDatabase database, MetaData metaData, File file) { this(database, metaData, file, new Defaults()); } + public BibDatabaseContext(Defaults defaults, DatabaseLocation location) { + this(new BibDatabase(), new MetaData(), defaults, location); + } + public BibDatabaseMode getMode() { Optional mode = metaData.getMode(); @@ -181,4 +195,28 @@ private String getFileDirectoryPath(String directoryName) { public List getFileDirectory() { return getFileDirectory(FieldName.FILE); } + + public DBMSSynchronizer getDBSynchronizer() { + return this.dbmsSynchronizer; + } + + public DatabaseLocation getLocation() { + return this.location; + } + + public void updateDatabaseLocation(DatabaseLocation newLocation) { + + if ((this.location == DatabaseLocation.SHARED) && (newLocation == DatabaseLocation.LOCAL)) { + this.database.unregisterListener(dbmsSynchronizer); + this.metaData.unregisterListener(dbmsSynchronizer); + } + + if (newLocation == DatabaseLocation.SHARED) { + this.dbmsSynchronizer = new DBMSSynchronizer(this); + this.database.registerListener(dbmsSynchronizer); + this.metaData.registerListener(dbmsSynchronizer); + } + + this.location = newLocation; + } } diff --git a/src/main/java/net/sf/jabref/MetaData.java b/src/main/java/net/sf/jabref/MetaData.java index 6ba45f4203a3..e59c5bb510dd 100644 --- a/src/main/java/net/sf/jabref/MetaData.java +++ b/src/main/java/net/sf/jabref/MetaData.java @@ -31,6 +31,8 @@ import java.util.TreeMap; import java.util.Vector; +import net.sf.jabref.event.GroupUpdatedEvent; +import net.sf.jabref.event.MetaDataChangedEvent; import net.sf.jabref.importer.fileformat.ParseException; import net.sf.jabref.logic.config.SaveOrderConfig; import net.sf.jabref.logic.exporter.FieldFormatterCleanups; @@ -43,8 +45,8 @@ import net.sf.jabref.logic.util.strings.StringUtil; import net.sf.jabref.model.database.BibDatabaseMode; import net.sf.jabref.model.entry.FieldName; -import net.sf.jabref.sql.DBStrings; +import com.google.common.eventbus.EventBus; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -66,14 +68,12 @@ public class MetaData implements Iterable { private final Map> metaData = new HashMap<>(); private GroupTreeNode groupsRoot; + private final EventBus eventBus = new EventBus(); private AbstractLabelPattern labelPattern; - private DBStrings dbStrings = new DBStrings(); - private Charset encoding = Globals.prefs.getDefaultEncoding(); - /** * The MetaData object stores all meta data sets in Vectors. To ensure that * the data is written correctly to string, the user of a meta data Vector @@ -82,7 +82,22 @@ public class MetaData implements Iterable { */ private MetaData(Map inData) throws ParseException { Objects.requireNonNull(inData); + setData(inData); + } + /** + * The MetaData object can be constructed with no data in it. + */ + public MetaData() { + // No data + } + + public static MetaData parse(Map data) throws ParseException { + return new MetaData(data); + } + + public void setData(Map inData) throws ParseException { + clearMetaData(); for (Map.Entry entry : inData.entrySet()) { StringReader data = new StringReader(entry.getValue()); List orderedData = new ArrayList<>(); @@ -98,25 +113,15 @@ private MetaData(Map inData) throws ParseException { if (GROUPSTREE.equals(entry.getKey())) { putGroups(orderedData); // the keys "groupsversion" and "groups" were used in JabRef versions around 1.3, we will not support them anymore + eventBus.post(new GroupUpdatedEvent(this)); } else if (SAVE_ACTIONS.equals(entry.getKey())) { - setSaveActions(FieldFormatterCleanups.parse(orderedData)); + metaData.put(SAVE_ACTIONS, FieldFormatterCleanups.parse(orderedData).getAsStringList()); // Without MetaDataChangedEvent } else { - putData(entry.getKey(), orderedData); + metaData.put(entry.getKey(), orderedData); } } } - /** - * The MetaData object can be constructed with no data in it. - */ - public MetaData() { - // No data - } - - public static MetaData parse(Map data) throws ParseException { - return new MetaData(data); - } - public Optional getSaveOrderConfig() { List storedSaveOrderConfig = getData(SAVE_ORDER_CONFIG); if (storedSaveOrderConfig != null) { @@ -161,7 +166,10 @@ public List getData(String key) { * @param key the key to remove */ public void remove(String key) { - metaData.remove(key); + if (metaData.containsKey(key)) { //otherwise redundant and disturbing events are going to be posted + metaData.remove(key); + postChange(); + } } /** @@ -172,6 +180,7 @@ public void remove(String key) { */ public void putData(String key, List orderedData) { metaData.put(key, orderedData); + postChange(); } /** @@ -182,6 +191,7 @@ public void putData(String key, List orderedData) { private void putGroups(List orderedData) throws ParseException { try { groupsRoot = GroupTreeNode.parse(orderedData, Globals.prefs); + eventBus.post(new GroupUpdatedEvent(this)); } catch (ParseException e) { throw new ParseException(Localization.lang( "Group tree could not be parsed. If you save the BibTeX database, all groups will be lost."), e); @@ -198,6 +208,7 @@ public GroupTreeNode getGroups() { */ public void setGroups(GroupTreeNode root) { groupsRoot = root; + eventBus.post(new GroupUpdatedEvent(this)); } /** @@ -225,14 +236,6 @@ private static Optional getNextUnit(Reader reader) throws IOException { return Optional.empty(); } - public DBStrings getDBStrings() { - return dbStrings; - } - - public void setDBStrings(DBStrings dbStrings) { - this.dbStrings = dbStrings; - } - /** * @return the stored label patterns */ @@ -390,7 +393,6 @@ public Map getAsStringMap() { } serializedMetaData.put(GROUPSTREE, stringBuilder.toString()); } - return serializedMetaData; } @@ -448,6 +450,13 @@ public void clearSaveOrderConfig() { remove(SAVE_ORDER_CONFIG); } + /** + * Posts a new {@link MetaDataChangedEvent} on the {@link EventBus}. + */ + public void postChange() { + eventBus.post(new MetaDataChangedEvent(this)); + } + /** * Returns the encoding used during parsing. */ @@ -458,4 +467,16 @@ public Charset getEncoding() { public void setEncoding(Charset encoding) { this.encoding = Objects.requireNonNull(encoding); } + + public void clearMetaData() { + metaData.clear(); + } + + public void registerListener(Object listener) { + this.eventBus.register(listener); + } + + public void unregisterListener(Object listener) { + this.eventBus.unregister(listener); + } } diff --git a/src/main/java/net/sf/jabref/event/GroupUpdatedEvent.java b/src/main/java/net/sf/jabref/event/GroupUpdatedEvent.java new file mode 100644 index 000000000000..8b44b78dd58c --- /dev/null +++ b/src/main/java/net/sf/jabref/event/GroupUpdatedEvent.java @@ -0,0 +1,19 @@ +package net.sf.jabref.event; + +import net.sf.jabref.MetaData; + +public class GroupUpdatedEvent { + + private final MetaData metaData; + + /** + * @param metaData Affected instance + */ + public GroupUpdatedEvent(MetaData metaData) { + this.metaData = metaData; + } + + public MetaData getMetaData() { + return this.metaData; + } +} diff --git a/src/main/java/net/sf/jabref/event/MetaDataChangedEvent.java b/src/main/java/net/sf/jabref/event/MetaDataChangedEvent.java new file mode 100644 index 000000000000..7a3c249144ad --- /dev/null +++ b/src/main/java/net/sf/jabref/event/MetaDataChangedEvent.java @@ -0,0 +1,22 @@ +package net.sf.jabref.event; + +import net.sf.jabref.MetaData; + +/** + * {@link MetaDataChangedEvent} is fired when a tuple of meta data has been put or removed. + */ +public class MetaDataChangedEvent { + + private final MetaData metaData; + + /** + * @param metaData Affected instance + */ + public MetaDataChangedEvent(MetaData metaData) { + this.metaData = metaData; + } + + public MetaData getMetaData() { + return this.metaData; + } +} diff --git a/src/main/java/net/sf/jabref/event/source/EntryEventSource.java b/src/main/java/net/sf/jabref/event/source/EntryEventSource.java new file mode 100644 index 000000000000..ced7c6c1d07f --- /dev/null +++ b/src/main/java/net/sf/jabref/event/source/EntryEventSource.java @@ -0,0 +1,26 @@ +/* Copyright (C) 2016 JabRef contributors. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +package net.sf.jabref.event.source; + +/** + * This enum represents the context EntryEvents were sent from. + */ +public enum EntryEventSource { + LOCAL, + SHARED, + UNDO, + SAVE_ACTION +} diff --git a/src/main/java/net/sf/jabref/gui/BasePanel.java b/src/main/java/net/sf/jabref/gui/BasePanel.java index 95c3d162c6f2..f1bfe851f0e3 100644 --- a/src/main/java/net/sf/jabref/gui/BasePanel.java +++ b/src/main/java/net/sf/jabref/gui/BasePanel.java @@ -60,6 +60,7 @@ import net.sf.jabref.collab.ChangeScanner; import net.sf.jabref.collab.FileUpdateListener; import net.sf.jabref.collab.FileUpdatePanel; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.external.AttachFileAction; import net.sf.jabref.external.ExternalFileMenuItem; import net.sf.jabref.external.ExternalFileType; @@ -126,6 +127,7 @@ import net.sf.jabref.logic.util.io.FileUtil; import net.sf.jabref.model.FieldChange; import net.sf.jabref.model.database.BibDatabase; +import net.sf.jabref.model.database.DatabaseLocation; import net.sf.jabref.model.database.KeyCollisionException; import net.sf.jabref.model.entry.BibEntry; import net.sf.jabref.model.entry.EntryType; @@ -135,6 +137,7 @@ import net.sf.jabref.model.event.EntryChangedEvent; import net.sf.jabref.preferences.HighlightMatchingGroupPreferences; import net.sf.jabref.preferences.JabRefPreferences; +import net.sf.jabref.shared.DBMSSynchronizer; import net.sf.jabref.specialfields.Printed; import net.sf.jabref.specialfields.Priority; import net.sf.jabref.specialfields.Quality; @@ -144,12 +147,6 @@ import net.sf.jabref.specialfields.SpecialFieldAction; import net.sf.jabref.specialfields.SpecialFieldDatabaseChangeListener; import net.sf.jabref.specialfields.SpecialFieldValue; -import net.sf.jabref.sql.DBConnectDialog; -import net.sf.jabref.sql.DBExporterAndImporterFactory; -import net.sf.jabref.sql.DBStrings; -import net.sf.jabref.sql.DbConnectAction; -import net.sf.jabref.sql.SQLUtil; -import net.sf.jabref.sql.exporter.DatabaseExporter; import ca.odell.glazedlists.event.ListEventListener; import com.google.common.eventbus.Subscribe; @@ -272,22 +269,28 @@ public ContentAutoCompleters getAutoCompleters() { public String getTabTitle() { StringBuilder title = new StringBuilder(); - - if (getBibDatabaseContext().getDatabaseFile() == null) { - title.append(GUIGlobals.UNTITLED_TITLE); - - if (getDatabase().hasEntries()) { - // if the database is not empty and no file is assigned, - // the database came from an import and has to be treated somehow - // -> mark as changed - // This also happens internally at basepanel to ensure consistency line 224 - title.append('*'); + DatabaseLocation databaseLocation = this.bibDatabaseContext.getLocation(); + + if (databaseLocation == DatabaseLocation.LOCAL) { + if (this.bibDatabaseContext.getDatabaseFile() == null) { + title.append(GUIGlobals.UNTITLED_TITLE); + + if (getDatabase().hasEntries()) { + // if the database is not empty and no file is assigned, + // the database came from an import and has to be treated somehow + // -> mark as changed + // This also happens internally at basepanel to ensure consistency line 224 + title.append('*'); + } + } else { + // check if file is modified + String changeFlag = isModified() ? "*" : ""; + title.append(this.bibDatabaseContext.getDatabaseFile().getName()).append(changeFlag); } - } else { - // check if file is modified - String changeFlag = isModified() ? "*" : ""; - title.append(getBibDatabaseContext().getDatabaseFile().getName()).append(changeFlag); + } else if (databaseLocation == DatabaseLocation.SHARED) { + title.append(this.bibDatabaseContext.getDBSynchronizer().getDBName() + " [shared]"); } + return title.toString(); } @@ -404,98 +407,6 @@ private void setupActions() { frame.groupToggle.setSelected(sidePaneManager.isComponentVisible("groups")); }); - // action for collecting database strings from user - actions.put(Actions.DB_CONNECT, new DbConnectAction(this)); - - // action for exporting database to external SQL database - actions.put(Actions.DB_EXPORT, new AbstractWorker() { - - String errorMessage = ""; - boolean connectedToDB; - - // run first, in EDT: - @Override - public void init() { - - DBStrings dbs = bibDatabaseContext.getMetaData().getDBStrings(); - - // get DBStrings from user if necessary - if (dbs.isConfigValid()) { - connectedToDB = true; - } else { - // init DB strings if necessary - if (!dbs.isInitialized()) { - dbs.initialize(); - } - - // show connection dialog - DBConnectDialog dbd = new DBConnectDialog(frame(), dbs); - dbd.setLocationRelativeTo(BasePanel.this); - dbd.setVisible(true); - - connectedToDB = dbd.isConnectedToDB(); - - // store database strings - if (connectedToDB) { - dbs = dbd.getDBStrings(); - bibDatabaseContext.getMetaData().setDBStrings(dbs); - dbd.dispose(); - } - } - } - - // run second, on a different thread: - @Override - public void run() { - if (!connectedToDB) { - return; - } - - final DBStrings dbs = bibDatabaseContext.getMetaData().getDBStrings(); - - try { - frame.output(Localization.lang("Attempting SQL export...")); - final DBExporterAndImporterFactory factory = new DBExporterAndImporterFactory(); - final DatabaseExporter exporter = factory.getExporter(dbs.getDbPreferences().getServerType()); - exporter.exportDatabaseToDBMS(bibDatabaseContext, getDatabase().getEntries(), dbs, frame); - dbs.isConfigValid(true); - } catch (Exception ex) { - final String preamble = Localization - .lang("Could not export to SQL database for the following reason:"); - errorMessage = SQLUtil.getExceptionMessage(ex); - LOGGER.info("Could not export to SQL database", ex); - dbs.isConfigValid(false); - JOptionPane.showMessageDialog(frame, preamble + '\n' + errorMessage, - Localization.lang("Export to SQL database"), JOptionPane.ERROR_MESSAGE); - } - - bibDatabaseContext.getMetaData().setDBStrings(dbs); - } - - // run third, on EDT: - @Override - public void update() { - - // if no error, report success - if (errorMessage.isEmpty()) { - if (connectedToDB) { - final DBStrings dbs = bibDatabaseContext.getMetaData().getDBStrings(); - frame.output(Localization.lang("%0 export successful", dbs.getDbPreferences().getServerType().getFormattedName())); - } - } else { // show an error dialog if an error occurred - final String preamble = Localization - .lang("Could not export to SQL database for the following reason:"); - frame.output(preamble + " " + errorMessage); - - JOptionPane.showMessageDialog(frame, preamble + '\n' + errorMessage, - Localization.lang("Export to SQL database"), JOptionPane.ERROR_MESSAGE); - - errorMessage = ""; - } - } - - }); - actions.put(FindUnlinkedFilesDialog.ACTION_COMMAND, (BaseAction) () -> { final FindUnlinkedFilesDialog dialog = new FindUnlinkedFilesDialog(frame, frame, BasePanel.this); dialog.setLocationRelativeTo(frame); @@ -652,6 +563,11 @@ public void update() { actions.put(Actions.OPEN_CONSOLE, (BaseAction) () -> JabRefDesktop .openConsole(frame.getCurrentBasePanel().getBibDatabaseContext().getDatabaseFile())); + actions.put(Actions.PULL_CHANGES_FROM_SHARED_DATABASE, (BaseAction) () -> { + DBMSSynchronizer dbmsSynchronizer = frame.getCurrentBasePanel().getBibDatabaseContext().getDBSynchronizer(); + dbmsSynchronizer.pullChanges(); + }); + actions.put(Actions.OPEN_URL, new OpenURLAction()); actions.put(Actions.MERGE_WITH_FETCHED_ENTRY, (BaseAction) () -> { @@ -1300,7 +1216,7 @@ public void run() { @Subscribe public void listen(EntryAddedEvent addedEntryEvent) { // if the added entry is an undo don't add it to the current group - if (addedEntryEvent.isUndo()){ + if (addedEntryEvent.getEntryEventSource() == EntryEventSource.UNDO) { scheduleUpdate(); return; } diff --git a/src/main/java/net/sf/jabref/gui/IconTheme.java b/src/main/java/net/sf/jabref/gui/IconTheme.java index 6b2ea150da39..1f006c36b50a 100644 --- a/src/main/java/net/sf/jabref/gui/IconTheme.java +++ b/src/main/java/net/sf/jabref/gui/IconTheme.java @@ -165,7 +165,7 @@ public enum JabRefIcon { FILE_MULTIPLE("\uf222"), /*css: file-multiple */ KEY_BINDINGS("\uf30c"), /*css: keyboard */ FIND_DUPLICATES("\uf16b"), /*css: code-equal */ - + PULL("\uf4c2"), /*source-pull*/ OPEN_IN_NEW_WINDOW("\uf3cc"), /*css: open-in-new */ CASE_SENSITIVE("\uf02c"), /* css: mdi-alphabetical */ REG_EX("\uf451"), /*css: mdi-regex */ diff --git a/src/main/java/net/sf/jabref/gui/JabRefFrame.java b/src/main/java/net/sf/jabref/gui/JabRefFrame.java index e755cd5f4d36..338f5803d8d6 100644 --- a/src/main/java/net/sf/jabref/gui/JabRefFrame.java +++ b/src/main/java/net/sf/jabref/gui/JabRefFrame.java @@ -85,6 +85,7 @@ import net.sf.jabref.gui.actions.NewEntryAction; import net.sf.jabref.gui.actions.NewSubDatabaseAction; import net.sf.jabref.gui.actions.OpenBrowserAction; +import net.sf.jabref.gui.actions.OpenSharedDatabaseAction; import net.sf.jabref.gui.actions.SearchForUpdateAction; import net.sf.jabref.gui.actions.SortTabsAction; import net.sf.jabref.gui.dbproperties.DatabasePropertiesDialog; @@ -130,6 +131,7 @@ import net.sf.jabref.logic.util.OS; import net.sf.jabref.logic.util.io.FileUtil; import net.sf.jabref.model.database.BibDatabaseMode; +import net.sf.jabref.model.database.DatabaseLocation; import net.sf.jabref.model.entry.BibEntry; import net.sf.jabref.model.entry.BibtexEntryTypes; import net.sf.jabref.model.entry.EntryType; @@ -142,7 +144,6 @@ import net.sf.jabref.specialfields.ReadStatus; import net.sf.jabref.specialfields.Relevance; import net.sf.jabref.specialfields.SpecialFieldsUtils; -import net.sf.jabref.sql.importer.DbImportAction; import com.google.common.eventbus.Subscribe; import com.jgoodies.looks.HeaderStyle; @@ -217,6 +218,7 @@ public class JabRefFrame extends JFrame implements OutputPrinter { private final AbstractAction selectKeys = new SelectKeysAction(); private final AbstractAction newBibtexDatabaseAction = new NewDatabaseAction(this, BibDatabaseMode.BIBTEX); private final AbstractAction newBiblatexDatabaseAction = new NewDatabaseAction(this, BibDatabaseMode.BIBLATEX); + private final AbstractAction openSharedDatabaseAction = new OpenSharedDatabaseAction(this); private final AbstractAction newSubDatabaseAction = new NewSubDatabaseAction(this); private final AbstractAction forkMeOnGitHubAction = new OpenBrowserAction("https://github.com/JabRef/jabref", Localization.menuTitle("Fork me on GitHub"), Localization.lang("Opens JabRef's GitHub page"), IconTheme.JabRefIcon.GITHUB.getSmallIcon(), IconTheme.JabRefIcon.GITHUB.getIcon()); @@ -272,6 +274,13 @@ public class JabRefFrame extends JFrame implements OutputPrinter { Localization.lang("Open terminal here"), Globals.getKeyPrefs().getKey(KeyBinding.OPEN_CONSOLE), IconTheme.JabRefIcon.CONSOLE.getIcon()); + + private final AbstractAction pullChangesFromSharedDatabase = new GeneralAction(Actions.PULL_CHANGES_FROM_SHARED_DATABASE, + Localization.menuTitle("Pull_changes_from_shared_database"), + Localization.lang("Pull_changes_from_shared_database"), + Globals.getKeyPrefs().getKey(KeyBinding.PULL_CHANGES_FROM_SHARED_DATABASE), + IconTheme.JabRefIcon.PULL.getIcon()); + private final AbstractAction mark = new GeneralAction(Actions.MARK_ENTRIES, Localization.menuTitle("Mark entries"), Localization.lang("Mark entries"), Globals.getKeyPrefs().getKey(KeyBinding.MARK_ENTRIES), IconTheme.JabRefIcon.MARK_ENTRIES.getIcon()); private final AbstractAction unmark = new GeneralAction(Actions.UNMARK_ENTRIES, @@ -423,14 +432,6 @@ public void actionPerformed(ActionEvent e) { private final AbstractAction bibtexKeyPattern = new BibtexKeyPatternAction(); private final AbstractAction errorConsole = new ErrorConsoleAction(this, Globals.getStreamEavesdropper(), GuiAppender.CACHE); - private final AbstractAction dbConnect = new GeneralAction(Actions.DB_CONNECT, - Localization.menuTitle("Connect to external SQL database"), - Localization.lang("Connect to external SQL database")); - - private final AbstractAction dbExport = new GeneralAction(Actions.DB_EXPORT, - Localization.menuTitle("Export to external SQL database"), - Localization.lang("Export to external SQL database")); - private final AbstractAction cleanupEntries = new GeneralAction(Actions.CLEANUP, Localization.menuTitle("Cleanup entries") + ELLIPSES, Localization.lang("Cleanup entries"), @@ -442,7 +443,6 @@ public void actionPerformed(ActionEvent e) { Localization.lang("Merge entries"), IconTheme.JabRefIcon.MERGE_ENTRIES.getIcon()); - private final AbstractAction dbImport = new DbImportAction(this).getAction(); private final AbstractAction downloadFullText = new GeneralAction(Actions.DOWNLOAD_FULL_TEXT, Localization.menuTitle("Look up full text document"), Localization.lang("Follow DOI or URL link and try to locate PDF full text document")); @@ -493,6 +493,8 @@ public void actionPerformed(ActionEvent e) { private final List openDatabaseOnlyActions = new LinkedList<>(); private final List severalDatabasesOnlyActions = new LinkedList<>(); private final List openAndSavedDatabasesOnlyActions = new LinkedList<>(); + private final List sharedDatabaseOnlyActions = new LinkedList<>(); + private final List noSharedDatabaseActions = new LinkedList<>(); private class EditModeAction extends AbstractAction { @@ -669,7 +671,7 @@ private void positionWindowOnScreen() { } } - private void refreshTitleAndTabs() { + public void refreshTitleAndTabs() { setWindowTitle(); updateAllTabTitles(); } @@ -690,11 +692,16 @@ public void setWindowTitle() { String modeInfo = String.format(" (%s)", Localization.lang("%0 mode", mode)); String changeFlag = panel.isModified() ? "*" : ""; - if (panel.getBibDatabaseContext().getDatabaseFile() == null) { - setTitle(FRAME_TITLE + " - " + GUIGlobals.UNTITLED_TITLE + changeFlag + modeInfo); - } else { - String databaseFile = panel.getBibDatabaseContext().getDatabaseFile().getPath(); - setTitle(FRAME_TITLE + " - " + databaseFile + changeFlag + modeInfo); + if (panel.getBibDatabaseContext().getLocation() == DatabaseLocation.LOCAL) { + if (panel.getBibDatabaseContext().getDatabaseFile() == null) { + setTitle(FRAME_TITLE + " - " + GUIGlobals.UNTITLED_TITLE + changeFlag + modeInfo); + } else { + String databaseFile = panel.getBibDatabaseContext().getDatabaseFile().getPath(); + setTitle(FRAME_TITLE + " - " + databaseFile + changeFlag + modeInfo); + } + } else if (panel.getBibDatabaseContext().getLocation() == DatabaseLocation.SHARED) { + setTitle(FRAME_TITLE + " - " + panel.getBibDatabaseContext().getDBSynchronizer().getDBName() + " [shared]" + + modeInfo); } } @@ -821,7 +828,8 @@ public boolean quit() { List filenames = new ArrayList<>(); if (tabbedPane.getTabCount() > 0) { for (int i = 0; i < tabbedPane.getTabCount(); i++) { - if (getBasePanelAt(i).isModified()) { + if (getBasePanelAt(i).isModified() + && (getBasePanelAt(i).getBibDatabaseContext().getLocation() == DatabaseLocation.LOCAL)) { tabbedPane.setSelectedIndex(i); String filename; @@ -1163,9 +1171,8 @@ private void fillMenu() { file.add(exportAll); file.add(exportSelected); file.addSeparator(); - file.add(dbConnect); - file.add(dbImport); - file.add(dbExport); + file.add(openSharedDatabaseAction); + file.add(pullChangesFromSharedDatabase); file.addSeparator(); file.add(databaseProperties); @@ -1445,6 +1452,7 @@ private void createToolBar() { tlb.addAction(makeKeyAction); tlb.addAction(cleanupEntries); tlb.addAction(mergeEntries); + tlb.addAction(pullChangesFromSharedDatabase); tlb.addAction(openConsole); tlb.addSeparator(); @@ -1519,7 +1527,7 @@ private void initActions() { dupliCheck, autoSetFile, newEntryAction, plainTextImport, getMassSetField(), getManageKeywords(), pushExternalButton.getMenuAction(), closeDatabaseAction, getSwitchPreviewAction(), checkIntegrity, toggleHighlightAny, toggleHighlightAll, toggleHighlightDisable, databaseProperties, abbreviateIso, abbreviateMedline, - unabbreviate, exportAll, exportSelected, importCurrent, saveAll, dbConnect, dbExport, focusTable, + unabbreviate, exportAll, exportSelected, importCurrent, saveAll, focusTable, toggleRelevance, toggleQualityAssured, togglePrinted, pushExternalButton.getComponent())); openDatabaseOnlyActions.addAll(newSpecificEntryAction); @@ -1531,6 +1539,8 @@ dupliCheck, autoSetFile, newEntryAction, plainTextImport, getMassSetField(), get .asList(nextTab, prevTab, sortTabs)); openAndSavedDatabasesOnlyActions.addAll(Collections.singletonList(openConsole)); + sharedDatabaseOnlyActions.addAll(Collections.singletonList(pullChangesFromSharedDatabase)); + noSharedDatabaseActions.addAll(Arrays.asList(save, saveAll)); tabbedPane.addChangeListener(event -> updateEnabledState()); @@ -1569,6 +1579,7 @@ public void updateEnabledState() { getBackAction().setEnabled(false); getForwardAction().setEnabled(false); setEnabled(openAndSavedDatabasesOnlyActions, false); + setEnabled(sharedDatabaseOnlyActions, false); } @@ -1578,6 +1589,9 @@ public void updateEnabledState() { boolean saved = current.getBibDatabaseContext().getDatabaseFile() != null; setEnabled(openAndSavedDatabasesOnlyActions, saved); } + boolean isShared = current.getBibDatabaseContext().getLocation() == DatabaseLocation.SHARED; + setEnabled(sharedDatabaseOnlyActions, isShared); + setEnabled(noSharedDatabaseActions, !isShared); } } @@ -1634,13 +1648,17 @@ public void updateAllTabTitles() { String uniqPath = paths.get(i); File file = getBasePanelAt(i).getBibDatabaseContext().getDatabaseFile(); - if ((file != null) && !uniqPath.equals(file.getName())) { - // remove filename - uniqPath = uniqPath.substring(0, uniqPath.lastIndexOf(File.separator)); - tabbedPane.setTitleAt(i, getBasePanelAt(i).getTabTitle() + " \u2014 " + uniqPath); - } else if ((file != null) && uniqPath.equals(file.getName())) { - // set original filename (again) + if (file == null) { tabbedPane.setTitleAt(i, getBasePanelAt(i).getTabTitle()); + } else { + if (!uniqPath.equals(file.getName())) { + // remove filename + uniqPath = uniqPath.substring(0, uniqPath.lastIndexOf(File.separator)); + tabbedPane.setTitleAt(i, getBasePanelAt(i).getTabTitle() + " \u2014 " + uniqPath); + } else { + // set original filename (again) + tabbedPane.setTitleAt(i, getBasePanelAt(i).getTabTitle()); + } } tabbedPane.setToolTipTextAt(i, file == null ? null : file.getAbsolutePath()); } @@ -2109,7 +2127,7 @@ private void closeTab(BasePanel panel) { return; } - if (panel.isModified()) { + if (panel.isModified() && (panel.getBibDatabaseContext().getLocation() == DatabaseLocation.LOCAL)) { if (confirmClose(panel)) { removeTab(panel); } @@ -2163,6 +2181,10 @@ private void removeTab(BasePanel panel) { updateAllTabTitles(); } + public void closeCurrentTab() { + removeTab(getCurrentBasePanel()); + } + public ManageKeywordsAction getManageKeywords() { return manageKeywords; } diff --git a/src/main/java/net/sf/jabref/gui/actions/Actions.java b/src/main/java/net/sf/jabref/gui/actions/Actions.java index d71bbe8644dc..ce5d68eb69b5 100644 --- a/src/main/java/net/sf/jabref/gui/actions/Actions.java +++ b/src/main/java/net/sf/jabref/gui/actions/Actions.java @@ -18,7 +18,6 @@ public class Actions { public static final String COPY_KEY_AND_TITLE = "copyKeyAndTitle"; public static final String CUT = "cut"; public static final String DB_CONNECT = "dbConnect"; - public static final String DB_EXPORT = "dbExport"; public static final String DELETE = "delete"; public static final String DOWNLOAD_FULL_TEXT = "downloadFullText"; public static final String DUPLI_CHECK = "dupliCheck"; @@ -41,6 +40,7 @@ public class Actions { public static final String OPEN_URL = "openUrl"; public static final String PASTE = "paste"; public static final String PLAIN_TEXT_IMPORT = "plainTextImport"; + public static final String PULL_CHANGES_FROM_SHARED_DATABASE = "pullChangesFromSharedDatabase"; public static final String REDO = "redo"; public static final String REMOVE_FROM_GROUP = "removeFromGroup"; public static final String REPLACE_ALL = "replaceAll"; diff --git a/src/main/java/net/sf/jabref/gui/actions/OpenSharedDatabaseAction.java b/src/main/java/net/sf/jabref/gui/actions/OpenSharedDatabaseAction.java new file mode 100644 index 000000000000..f8c99a3ab351 --- /dev/null +++ b/src/main/java/net/sf/jabref/gui/actions/OpenSharedDatabaseAction.java @@ -0,0 +1,32 @@ +package net.sf.jabref.gui.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.Action; + +import net.sf.jabref.gui.JabRefFrame; +import net.sf.jabref.gui.shared.OpenSharedDatabaseDialog; +import net.sf.jabref.logic.l10n.Localization; + +/** + * The action concerned with opening a shared database. + */ +public class OpenSharedDatabaseAction extends MnemonicAwareAction { + + private final JabRefFrame jabRefFrame; + + + public OpenSharedDatabaseAction(JabRefFrame jabRefFrame) { + super(); + this.jabRefFrame = jabRefFrame; + putValue(Action.NAME, Localization.menuTitle("Open shared database")); + putValue(Action.SHORT_DESCRIPTION, Localization.lang("Open shared database")); + } + + @Override + public void actionPerformed(ActionEvent e) { + + OpenSharedDatabaseDialog openSharedDatabaseDialog = new OpenSharedDatabaseDialog(jabRefFrame); + openSharedDatabaseDialog.setVisible(true); + } +} diff --git a/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditor.java index e179cab4129b..553af4289dc4 100644 --- a/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditor.java @@ -208,6 +208,7 @@ public class EntryEditor extends JPanel implements EntryContainer { private final TabListener tabListener = new TabListener(); + public EntryEditor(JabRefFrame frame, BasePanel panel, BibEntry entry) { this.frame = frame; this.panel = panel; diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java b/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java index db25ab211bdf..8bb1753ac8b9 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java @@ -59,6 +59,7 @@ import net.sf.jabref.Globals; import net.sf.jabref.MetaData; +import net.sf.jabref.event.GroupUpdatedEvent; import net.sf.jabref.gui.BasePanel; import net.sf.jabref.gui.IconTheme; import net.sf.jabref.gui.JabRefFrame; @@ -82,6 +83,7 @@ import net.sf.jabref.model.entry.BibEntry; import net.sf.jabref.preferences.JabRefPreferences; +import com.google.common.eventbus.Subscribe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -262,6 +264,7 @@ public void stateChanged(ChangeEvent e) { groupsRoot.addNewGroup(newGroup, panel.getUndoManager()); panel.markBaseChanged(); frame.output(Localization.lang("Created group \"%0\".", newGroup.getName())); + panel.getBibDatabaseContext().getMetaData().postChange(); } }); andCb.addActionListener(e -> valueChanged(null)); @@ -792,13 +795,14 @@ public void actionPerformed(ActionEvent e) { Localization.lang("Assign the original group's entries to this group?"), Localization.lang("Change of Grouping Method"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); - boolean keepPreviousAssignments = i == JOptionPane.YES_OPTION && + boolean keepPreviousAssignments = (i == JOptionPane.YES_OPTION) && WarnAssignmentSideEffects.warnAssignmentSideEffects(newGroup, panel.frame()); AbstractUndoableEdit undoAddPreviousEntries = null; UndoableModifyGroup undo = new UndoableModifyGroup(GroupSelector.this, groupsRoot, node, newGroup); Optional addChange = node.getNode().setGroup(newGroup, keepPreviousAssignments, panel.getDatabase().getEntries()); + panel.getBibDatabaseContext().getMetaData().postChange(); if (addChange.isPresent()) { undoAddPreviousEntries = UndoableChangeEntriesOfGroup.getUndoableEdit(null, addChange.get()); } @@ -877,6 +881,7 @@ public void actionPerformed(ActionEvent e) { panel.getUndoManager().addEdit(undo); panel.markBaseChanged(); frame.output(Localization.lang("Added group \"%0\".", newGroup.getName())); + panel.getBibDatabaseContext().getMetaData().postChange(); } } @@ -901,6 +906,7 @@ public void actionPerformed(ActionEvent e) { panel.getUndoManager().addEdit(undo); panel.markBaseChanged(); frame.output(Localization.lang("Removed group \"%0\" and its subgroups.", group.getName())); + panel.getBibDatabaseContext().getMetaData().postChange(); } } } @@ -926,6 +932,7 @@ public void actionPerformed(ActionEvent e) { panel.getUndoManager().addEdit(undo); panel.markBaseChanged(); frame.output(Localization.lang("Removed all subgroups of group \"%0\".", node.getName())); + panel.getBibDatabaseContext().getMetaData().postChange(); } } } @@ -953,6 +960,7 @@ public void actionPerformed(ActionEvent e) { panel.getUndoManager().addEdit(undo); panel.markBaseChanged(); frame.output(Localization.lang("Removed group \"%0\".", group.getName())); + panel.getBibDatabaseContext().getMetaData().postChange(); } } } @@ -978,6 +986,7 @@ public void actionPerformed(ActionEvent ae) { panel.getUndoManager().addEdit(undo); panel.markBaseChanged(); frame.output(Localization.lang("Sorted immediate subgroups.")); + panel.getBibDatabaseContext().getMetaData().postChange(); } } @@ -996,6 +1005,7 @@ public void actionPerformed(ActionEvent ae) { panel.getUndoManager().addEdit(undo); panel.markBaseChanged(); frame.output(Localization.lang("Sorted all subgroups recursively.")); + panel.getBibDatabaseContext().getMetaData().postChange(); } } @@ -1224,6 +1234,8 @@ public void setActiveBasePanel(BasePanel panel) { setGroups(metaData.getGroups()); } + metaData.registerListener(this); + synchronized (getTreeLock()) { validateTree(); } @@ -1262,4 +1274,8 @@ public GroupsTree getGroupsTree() { return this.groupsTree; } + @Subscribe + public void listen(GroupUpdatedEvent updateEvent) { + setGroups(updateEvent.getMetaData().getGroups()); + } } diff --git a/src/main/java/net/sf/jabref/gui/keyboard/KeyBinding.java b/src/main/java/net/sf/jabref/gui/keyboard/KeyBinding.java index 7399218a8407..d4253125a806 100644 --- a/src/main/java/net/sf/jabref/gui/keyboard/KeyBinding.java +++ b/src/main/java/net/sf/jabref/gui/keyboard/KeyBinding.java @@ -61,6 +61,7 @@ public enum KeyBinding { OPEN_FOLDER("Open folder", Localization.lang("Open folder"), "ctrl shift O"), OPEN_URL_OR_DOI("Open URL or DOI", Localization.lang("Open URL or DOI"), "F3"), PASTE("Paste", Localization.lang("Paste"), "ctrl V"), + PULL_CHANGES_FROM_SHARED_DATABASE("Pull changes from shared database", Localization.lang("Pull changes from shared database"), "ctrl shift R"), PREAMBLE_EDITOR_STORE_CHANGES("Preamble editor, store changes", Localization.lang("Preamble editor, store changes"), "alt S"), PREVIOUS_TAB("Previous tab", Localization.lang("Previous tab"), "ctrl PAGE_UP"), PRINT_ENTRY_PREVIEW("Print entry preview", Localization.lang("Print entry preview"), "alt P"), diff --git a/src/main/java/net/sf/jabref/gui/shared/MergeSharedEntryDialog.java b/src/main/java/net/sf/jabref/gui/shared/MergeSharedEntryDialog.java new file mode 100644 index 000000000000..33c235009c39 --- /dev/null +++ b/src/main/java/net/sf/jabref/gui/shared/MergeSharedEntryDialog.java @@ -0,0 +1,112 @@ +package net.sf.jabref.gui.shared; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import net.sf.jabref.gui.JabRefFrame; +import net.sf.jabref.gui.mergeentries.MergeEntries; +import net.sf.jabref.logic.l10n.Localization; +import net.sf.jabref.model.database.BibDatabaseMode; +import net.sf.jabref.model.entry.BibEntry; +import net.sf.jabref.shared.DBMSSynchronizer; + +public class MergeSharedEntryDialog { + + private final JabRefFrame jabRefFrame; + private final DBMSSynchronizer dbmsSynchronizer; + private final BibEntry localBibEntry; + private final BibEntry sharedBibEntry; + private final JDialog mergeDialog; + private final MergeEntries mergeEntries; + + + public MergeSharedEntryDialog(JabRefFrame jabRefFrame, DBMSSynchronizer dbmsSynchronizer, BibEntry localBibEntry, + BibEntry sharedBibEntry, BibDatabaseMode bibDatabaseMode) { + this.jabRefFrame = jabRefFrame; + this.dbmsSynchronizer = dbmsSynchronizer; + this.localBibEntry = localBibEntry; + this.sharedBibEntry = sharedBibEntry; + this.mergeDialog = new JDialog(jabRefFrame, Localization.lang("Update refused"), true); + this.mergeEntries = new MergeEntries(sharedBibEntry, localBibEntry, Localization.lang("Shared entry"), + Localization.lang("Local entry"), bibDatabaseMode); + } + + public void showMergeDialog() { + + mergeDialog.setMinimumSize(new Dimension(600, 600)); + + StringBuilder message = new StringBuilder(); + message.append(""); + message.append(""); + message.append(Localization.lang("Update could not be performed due to existing change conflicts.")); + message.append(""); + message.append("

"); + message.append(Localization.lang("You are not working on the newest version of BibEntry.")); + message.append("

"); + message.append(Localization.lang("Shared version: %0", String.valueOf(sharedBibEntry.getSharedBibEntryData().getVersion()))); + message.append("
"); + message.append(Localization.lang("Local version: %0", String.valueOf(localBibEntry.getSharedBibEntryData().getVersion()))); + message.append("

"); + message.append(Localization.lang("Please merge the shared entry with yours and press \"Merge entries\" to resolve this problem.")); + message.append("
"); + + JLabel mergeInnformation = new JLabel(message.toString()); + mergeInnformation.setBorder(new EmptyBorder(9, 9, 9, 9)); + + mergeDialog.add(mergeInnformation, BorderLayout.NORTH); + mergeDialog.add(mergeEntries.getMergeEntryPanel(), BorderLayout.CENTER); + + JButton mergeButton = new JButton(Localization.lang("Merge entries")); + mergeButton.addActionListener(e -> mergeEntries()); + + JButton cancelButton = new JButton(Localization.lang("Cancel")); + cancelButton.addActionListener(e -> showConfirmationDialog()); + + JPanel buttonPanel = new JPanel(); + buttonPanel.add(mergeButton, BorderLayout.WEST); + buttonPanel.add(cancelButton, BorderLayout.EAST); + + mergeDialog.add(buttonPanel, BorderLayout.SOUTH); + mergeDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + mergeDialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + showConfirmationDialog(); + } + }); + + mergeDialog.setLocationRelativeTo(jabRefFrame); + mergeDialog.pack(); + mergeDialog.setVisible(true); + } + + private void showConfirmationDialog() { + int answer = JOptionPane.showConfirmDialog(mergeDialog, + Localization.lang("Canceling this operation will leave your changes unsynchronized. Cancel anyway?"), + Localization.lang("Warning"), JOptionPane.YES_NO_OPTION); + + if (answer == 0) { + mergeDialog.dispose(); + } + } + + private void mergeEntries() { + BibEntry mergedBibEntry = mergeEntries.getMergeEntry(); + mergedBibEntry.getSharedBibEntryData().setSharedID(sharedBibEntry.getSharedBibEntryData().getSharedID()); + mergedBibEntry.getSharedBibEntryData().setVersion(sharedBibEntry.getSharedBibEntryData().getVersion()); + + mergeDialog.dispose(); // dispose before synchronizing to avoid multiple merge windows in case of new conflict. + + dbmsSynchronizer.synchronizeSharedEntry(mergedBibEntry); + dbmsSynchronizer.synchronizeLocalDatabase(); + } +} \ No newline at end of file diff --git a/src/main/java/net/sf/jabref/gui/shared/OpenSharedDatabaseDialog.java b/src/main/java/net/sf/jabref/gui/shared/OpenSharedDatabaseDialog.java new file mode 100644 index 000000000000..1def93f3f7a4 --- /dev/null +++ b/src/main/java/net/sf/jabref/gui/shared/OpenSharedDatabaseDialog.java @@ -0,0 +1,339 @@ +/* Copyright (C) 2003-2016 JabRef contributors. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +package net.sf.jabref.gui.shared; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.sql.SQLException; +import java.util.Optional; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.KeyStroke; + +import net.sf.jabref.BibDatabaseContext; +import net.sf.jabref.Defaults; +import net.sf.jabref.Globals; +import net.sf.jabref.JabRefException; +import net.sf.jabref.gui.JabRefFrame; +import net.sf.jabref.logic.l10n.Localization; +import net.sf.jabref.model.database.BibDatabaseMode; +import net.sf.jabref.model.database.DatabaseLocation; +import net.sf.jabref.shared.DBMSConnectionProperties; +import net.sf.jabref.shared.DBMSConnector; +import net.sf.jabref.shared.DBMSType; + +import com.jgoodies.forms.builder.ButtonBarBuilder; + +public class OpenSharedDatabaseDialog extends JDialog { + + private final JabRefFrame frame; + + private final GridBagLayout gridBagLayout = new GridBagLayout(); + private final GridBagConstraints gridBagConstraints = new GridBagConstraints(); + private final JPanel connectionPanel = new JPanel(); + private final JPanel buttonPanel = new JPanel(); + + private final JLabel databaseTypeLabel = new JLabel(Localization.lang("Database type") + ":"); + private final JLabel hostPortLabel = new JLabel(Localization.lang("Host") + "/" + Localization.lang("Port") + ":"); + private final JLabel databaseLabel = new JLabel(Localization.lang("Database") + ":"); + private final JLabel userLabel = new JLabel(Localization.lang("User") + ":"); + private final JLabel passwordLabel = new JLabel(Localization.lang("Password") + ":"); + + private final JTextField hostField = new JTextField(12); + private final JTextField portField = new JTextField(4); + private final JTextField userField = new JTextField(14); + private final JTextField databaseField = new JTextField(14); + + private final JPasswordField passwordField = new JPasswordField(14); + private final JComboBox dbmsTypeDropDown = new JComboBox<>(); + + private final JButton connectButton = new JButton(Localization.lang("Connect")); + private final JButton cancelButton = new JButton(Localization.lang("Cancel")); + + private static final String SHARED_DATABASE_TYPE = "sharedDatabaseType"; + private static final String SHARED_DATABASE_HOST = "sharedDatabaseHost"; + private static final String SHARED_DATABASE_PORT = "sharedDatabasePort"; + private static final String SHARED_DATABASE_NAME = "sharedDatabaseName"; + private static final String SHARED_DATABASE_USER = "sharedDatabaseUser"; + + + /** + * @param frame the JabRef Frame + */ + public OpenSharedDatabaseDialog(JabRefFrame frame) { + super(frame, Localization.lang("Open shared database")); + this.frame = frame; + initLayout(); + applyGlobalPrefs(); + setupActions(); + pack(); + setLocationRelativeTo(frame); + } + + /** + * Defines and sets the different actions up. + */ + private void setupActions() { + + Action openAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + try { + checkFields(); + BibDatabaseMode selectedMode = Globals.prefs.getDefaultBibDatabaseMode(); + + BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new Defaults(selectedMode), + DatabaseLocation.SHARED); + + DBMSConnectionProperties connectionProperties = new DBMSConnectionProperties(); + connectionProperties.setType((DBMSType) dbmsTypeDropDown.getSelectedItem()); + connectionProperties.setHost(hostField.getText()); + connectionProperties.setPort(Integer.parseInt(portField.getText())); + connectionProperties.setDatabase(databaseField.getText()); + connectionProperties.setUser(userField.getText()); + connectionProperties.setPassword(new String(passwordField.getPassword())); //JPasswordField.getPassword() does not return a String, but a char array. + + bibDatabaseContext.getDBSynchronizer().openSharedDatabase(connectionProperties); + + frame.addTab(bibDatabaseContext, true); + + setGlobalPrefs(); + + bibDatabaseContext.getDBSynchronizer().registerListener(new SharedDatabaseUIManager(frame)); + frame.output(Localization.lang("Connection_to_%0_server_stablished.", connectionProperties.getType().toString())); + dispose(); + } catch (ClassNotFoundException exception) { + JOptionPane.showMessageDialog(OpenSharedDatabaseDialog.this, exception.getMessage(), Localization.lang("Driver error"), + JOptionPane.ERROR_MESSAGE); + } catch (SQLException exception) { + JOptionPane.showMessageDialog(OpenSharedDatabaseDialog.this, exception.getMessage(), + Localization.lang("Connection error"), JOptionPane.ERROR_MESSAGE); + } catch (JabRefException exception) { + JOptionPane.showMessageDialog(OpenSharedDatabaseDialog.this, exception.getMessage(), + Localization.lang("Warning"), JOptionPane.WARNING_MESSAGE); + } + } + }; + connectButton.addActionListener(openAction); + + Action cancelAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }; + cancelButton.addActionListener(cancelAction); + + /** + * Set up a listener which updates the default port number once the selection in dbmsTypeDropDown has changed. + */ + Action dbmsTypeDropDownAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + portField.setText(Integer.toString(((DBMSType) dbmsTypeDropDown.getSelectedItem()).getDefaultPort())); + } + }; + dbmsTypeDropDown.addActionListener(dbmsTypeDropDownAction); + + // Add enter button action listener + connectButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), + "Enter_pressed"); + connectButton.getActionMap().put("Enter_pressed", openAction); + + } + + /** + * Fetches possibly saved data and configures the control elements respectively. + */ + private void applyGlobalPrefs() { + Optional sharedDatabaseType = Globals.prefs.getAsOptional(SHARED_DATABASE_TYPE); + Optional sharedDatabaseHost = Globals.prefs.getAsOptional(SHARED_DATABASE_HOST); + Optional sharedDatabasePort = Globals.prefs.getAsOptional(SHARED_DATABASE_PORT); + Optional sharedDatabaseName = Globals.prefs.getAsOptional(SHARED_DATABASE_NAME); + Optional sharedDatabaseUser = Globals.prefs.getAsOptional(SHARED_DATABASE_USER); + + if (sharedDatabaseType.isPresent()) { + Optional dbmsType = DBMSType.fromString(sharedDatabaseType.get()); + if (dbmsType.isPresent()) { + dbmsTypeDropDown.setSelectedItem(dbmsType.get()); + } + } + + if (sharedDatabaseHost.isPresent()) { + hostField.setText(sharedDatabaseHost.get()); + } + + if (sharedDatabasePort.isPresent()) { + portField.setText(sharedDatabasePort.get()); + } else { + portField.setText(Integer.toString(((DBMSType) dbmsTypeDropDown.getSelectedItem()).getDefaultPort())); + } + + if (sharedDatabaseName.isPresent()) { + databaseField.setText(sharedDatabaseName.get()); + } + + if (sharedDatabaseUser.isPresent()) { + userField.setText(sharedDatabaseUser.get()); + } + } + + /** + * Set up the layout and position the control units in their right place. + */ + private void initLayout() { + + setResizable(false); + + Insets defautInsets = new Insets(4, 15, 4, 4); + + connectionPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), Localization.lang("Connection"))); + connectionPanel.setLayout(gridBagLayout); + + Set availableDBMSTypes = DBMSConnector.getAvailableDBMSTypes(); + DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel<>( + availableDBMSTypes.toArray(new DBMSType[availableDBMSTypes.size()])); + + dbmsTypeDropDown.setModel(comboBoxModel); + + gridBagConstraints.insets = defautInsets; + gridBagConstraints.fill = GridBagConstraints.BOTH; + gridBagLayout.setConstraints(connectionPanel, gridBagConstraints); + + //1. column + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + connectionPanel.add(databaseTypeLabel, gridBagConstraints); + + gridBagConstraints.gridy = 1; + connectionPanel.add(hostPortLabel, gridBagConstraints); + + gridBagConstraints.gridy = 2; + connectionPanel.add(databaseLabel, gridBagConstraints); + + gridBagConstraints.gridy = 3; + connectionPanel.add(userLabel, gridBagConstraints); + + gridBagConstraints.gridy = 4; + connectionPanel.add(passwordLabel, gridBagConstraints); + + // 2. column + gridBagConstraints.gridwidth = 2; + + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + connectionPanel.add(dbmsTypeDropDown, gridBagConstraints); + + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 1; // the hostField is smaller than the others. + gridBagConstraints.insets = new Insets(4, 15, 4, 0); + connectionPanel.add(hostField, gridBagConstraints); + + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.insets = defautInsets; + connectionPanel.add(databaseField, gridBagConstraints); + + gridBagConstraints.gridy = 3; + connectionPanel.add(userField, gridBagConstraints); + + gridBagConstraints.gridy = 4; + connectionPanel.add(passwordField, gridBagConstraints); + + // 3. column + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 1; + gridBagConstraints.insets = new Insets(4, 0, 4, 4); + connectionPanel.add(portField, gridBagConstraints); + + gridBagConstraints.insets = new Insets(4, 4, 4, 4); + + ButtonBarBuilder bsb = new ButtonBarBuilder(buttonPanel); + bsb.addGlue(); + bsb.addButton(connectButton); + bsb.addRelatedGap(); + bsb.addButton(cancelButton); + + getContentPane().setLayout(gridBagLayout); + + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagLayout.setConstraints(connectionPanel, gridBagConstraints); + getContentPane().add(connectionPanel); + gridBagConstraints.gridy = 1; + gridBagConstraints.insets = new Insets(10, 0, 12, 13); + gridBagLayout.setConstraints(buttonPanel, gridBagConstraints); + getContentPane().add(buttonPanel); + + setModal(true); // Owner window should be disabled while this dialog is opened. + } + + /** + * Saves the data from this dialog persistently to facilitate the usage. + */ + public void setGlobalPrefs() { + Globals.prefs.put(SHARED_DATABASE_TYPE, ((DBMSType) dbmsTypeDropDown.getSelectedItem()).toString()); + Globals.prefs.put(SHARED_DATABASE_HOST, hostField.getText()); + Globals.prefs.put(SHARED_DATABASE_PORT, portField.getText()); + Globals.prefs.put(SHARED_DATABASE_NAME, databaseField.getText()); + Globals.prefs.put(SHARED_DATABASE_USER, userField.getText()); + } + + private boolean isEmptyField(JTextField field) { + return field.getText().trim().length() == 0; + } + + /** + * Checks every required text field for emptiness. + */ + private void checkFields() throws JabRefException { + if (isEmptyField(hostField)) { + hostField.requestFocus(); + throw new JabRefException(Localization.lang("Required_field_\"%0\"_is_empty.", Localization.lang("Host"))); + } + if (isEmptyField(portField)) { + portField.requestFocus(); + throw new JabRefException(Localization.lang("Required_field_\"%0\"_is_empty.", Localization.lang("Port"))); + } + if (isEmptyField(databaseField)) { + databaseField.requestFocus(); + throw new JabRefException( + Localization.lang("Required_field_\"%0\"_is_empty.", Localization.lang("Database"))); + } + if (isEmptyField(userField)) { + userField.requestFocus(); + throw new JabRefException(Localization.lang("Required_field_\"%0\"_is_empty.", Localization.lang("User"))); + } + } + +} diff --git a/src/main/java/net/sf/jabref/gui/shared/SharedDatabaseUIManager.java b/src/main/java/net/sf/jabref/gui/shared/SharedDatabaseUIManager.java new file mode 100644 index 000000000000..60322e63a9ce --- /dev/null +++ b/src/main/java/net/sf/jabref/gui/shared/SharedDatabaseUIManager.java @@ -0,0 +1,83 @@ +package net.sf.jabref.gui.shared; + +import javax.swing.JOptionPane; + +import net.sf.jabref.gui.JabRefFrame; +import net.sf.jabref.logic.l10n.Localization; +import net.sf.jabref.model.database.DatabaseLocation; +import net.sf.jabref.model.entry.BibEntry; +import net.sf.jabref.shared.DBMSSynchronizer; +import net.sf.jabref.shared.event.ConnectionLostEvent; +import net.sf.jabref.shared.event.SharedEntryNotPresentEvent; +import net.sf.jabref.shared.event.UpdateRefusedEvent; + +import com.google.common.eventbus.Subscribe; + +public class SharedDatabaseUIManager { + + private final JabRefFrame jabRefFrame; + private final DBMSSynchronizer dbmsSynchronizer; + + public SharedDatabaseUIManager(JabRefFrame jabRefFrame) { + this.jabRefFrame = jabRefFrame; + this.dbmsSynchronizer = jabRefFrame.getCurrentBasePanel().getBibDatabaseContext().getDBSynchronizer(); + } + + @Subscribe + public void listen(ConnectionLostEvent connectionLostEvent) { + + jabRefFrame.output(Localization.lang("Connection lost.")); + + String[] options = {Localization.lang("Reconnect"), Localization.lang("Work offline"), + Localization.lang("Close database")}; + + int answer = JOptionPane.showOptionDialog(jabRefFrame, + Localization.lang("The connection to the server has been determinated.") + "\n\n", + Localization.lang("Connection lost"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + + if (answer == 0) { + jabRefFrame.closeCurrentTab(); + OpenSharedDatabaseDialog openSharedDatabaseDialog = new OpenSharedDatabaseDialog(jabRefFrame); + openSharedDatabaseDialog.setVisible(true); + } else if (answer == 1) { + connectionLostEvent.getBibDatabaseContext().updateDatabaseLocation(DatabaseLocation.LOCAL); + jabRefFrame.refreshTitleAndTabs(); + jabRefFrame.updateEnabledState(); + jabRefFrame.output(Localization.lang("Working offline.")); + } else { + jabRefFrame.closeCurrentTab(); + } + } + + @Subscribe + public void listen(UpdateRefusedEvent updateRefusedEvent) { + + jabRefFrame.output(Localization.lang("Update refused.")); + + new MergeSharedEntryDialog(jabRefFrame, dbmsSynchronizer, updateRefusedEvent.getLocalBibEntry(), + updateRefusedEvent.getSharedBibEntry(), + updateRefusedEvent.getBibDatabaseContext().getMode()).showMergeDialog(); + } + + @Subscribe + public void listen(SharedEntryNotPresentEvent sharedEntryNotPresentEvent) { + BibEntry bibEntry = sharedEntryNotPresentEvent.getBibEntry(); + + String[] options = {Localization.lang("Keep"), Localization.lang("Delete")}; + + int answer = JOptionPane.showOptionDialog(jabRefFrame, + Localization.lang("The BibEntry you currently work on has been deleted on the shared side. " + + "Hit \"Keep\" to recover the entry.") + "\n\n", + Localization.lang("Update refused"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, + null, options, options[0]); + + if (answer == 0) { + dbmsSynchronizer.getDBProcessor().insertEntry(bibEntry); + } else if (answer == 1) { + jabRefFrame.getCurrentBasePanel().hideBottomComponent(); + } + dbmsSynchronizer.pullChanges(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/sf/jabref/gui/undo/UndoableRemoveEntry.java b/src/main/java/net/sf/jabref/gui/undo/UndoableRemoveEntry.java index 45a1125d0fe1..dbff6c28ebe4 100644 --- a/src/main/java/net/sf/jabref/gui/undo/UndoableRemoveEntry.java +++ b/src/main/java/net/sf/jabref/gui/undo/UndoableRemoveEntry.java @@ -15,6 +15,7 @@ */ package net.sf.jabref.gui.undo; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.gui.BasePanel; import net.sf.jabref.logic.l10n.Localization; import net.sf.jabref.logic.util.strings.StringUtil; @@ -54,7 +55,7 @@ public String getPresentationName() { @Override public void undo() { super.undo(); - base.insertEntry(entry, true); + base.insertEntry(entry, EntryEventSource.UNDO); } @Override diff --git a/src/main/java/net/sf/jabref/logic/cleanup/FieldFormatterCleanup.java b/src/main/java/net/sf/jabref/logic/cleanup/FieldFormatterCleanup.java index 14c8a1c3304d..6e479023aedd 100644 --- a/src/main/java/net/sf/jabref/logic/cleanup/FieldFormatterCleanup.java +++ b/src/main/java/net/sf/jabref/logic/cleanup/FieldFormatterCleanup.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Objects; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.logic.formatter.Formatter; import net.sf.jabref.model.FieldChange; import net.sf.jabref.model.entry.BibEntry; @@ -70,7 +71,7 @@ private List cleanupSingleField(String fieldKey, BibEntry entry) { entry.clearField(fieldKey); newValue = null; } else { - entry.setField(fieldKey, newValue); + entry.setField(fieldKey, newValue, EntryEventSource.SAVE_ACTION); } FieldChange change = new FieldChange(entry, fieldKey, oldValue, newValue); return Collections.singletonList(change); diff --git a/src/main/java/net/sf/jabref/logic/exporter/BibDatabaseWriter.java b/src/main/java/net/sf/jabref/logic/exporter/BibDatabaseWriter.java index 0561174839b6..f4327ac719a8 100644 --- a/src/main/java/net/sf/jabref/logic/exporter/BibDatabaseWriter.java +++ b/src/main/java/net/sf/jabref/logic/exporter/BibDatabaseWriter.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -79,6 +80,10 @@ private static List applySaveActions(List toChange, MetaD return changes; } + public static List applySaveActions(BibEntry entry, MetaData metaData) { + return applySaveActions(Arrays.asList(entry), metaData); + } + private static List> getSaveComparators(SavePreferences preferences, MetaData metaData) { List> comparators = new ArrayList<>(); diff --git a/src/main/java/net/sf/jabref/logic/exporter/FieldFormatterCleanups.java b/src/main/java/net/sf/jabref/logic/exporter/FieldFormatterCleanups.java index eeec893f512c..dc3de530c8a1 100644 --- a/src/main/java/net/sf/jabref/logic/exporter/FieldFormatterCleanups.java +++ b/src/main/java/net/sf/jabref/logic/exporter/FieldFormatterCleanups.java @@ -86,7 +86,7 @@ public int hashCode() { return Objects.hash(actions, enabled); } - private static List parse(String formatterString) { + public static List parse(String formatterString) { if ((formatterString == null) || formatterString.isEmpty()) { // no save actions defined in the meta data diff --git a/src/main/java/net/sf/jabref/logic/util/Version.java b/src/main/java/net/sf/jabref/logic/util/Version.java index 87f7c7b46664..fbdcd1edd4ca 100644 --- a/src/main/java/net/sf/jabref/logic/util/Version.java +++ b/src/main/java/net/sf/jabref/logic/util/Version.java @@ -46,7 +46,8 @@ public class Version { * @param version must be in form of X.X (e.g., 3.3; 3.4dev) */ public Version(String version) { - if ((version == null) || "".equals(version) || version.equals(BuildInfo.UNKNOWN_VERSION)) { + if ((version == null) || "".equals(version) || version.equals(BuildInfo.UNKNOWN_VERSION) + || "${version}".equals(version)) { return; } diff --git a/src/main/java/net/sf/jabref/model/database/BibDatabase.java b/src/main/java/net/sf/jabref/model/database/BibDatabase.java index ce46947e5540..29167d619b18 100644 --- a/src/main/java/net/sf/jabref/model/database/BibDatabase.java +++ b/src/main/java/net/sf/jabref/model/database/BibDatabase.java @@ -43,6 +43,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.model.entry.BibEntry; import net.sf.jabref.model.entry.BibtexString; import net.sf.jabref.model.entry.EntryUtil; @@ -167,24 +168,24 @@ public synchronized List getEntriesByKey(String key) { * Inserts the entry, given that its ID is not already in use. * use Util.createId(...) to make up a unique ID for an entry. * - * @param entry the entry to insert into the database + * @param entry BibEntry to insert into the database * @return false if the insert was done without a duplicate warning * @throws KeyCollisionException thrown if the entry id ({@link BibEntry#getId()}) is already present in the database */ public synchronized boolean insertEntry(BibEntry entry) throws KeyCollisionException { - return this.insertEntry(entry, false); + return insertEntry(entry, EntryEventSource.LOCAL); } /** * Inserts the entry, given that its ID is not already in use. * use Util.createId(...) to make up a unique ID for an entry. * - * @param entry the entry to insert into the database - * @param isUndo set to true if the insertion is caused by an undo + * @param entry BibEntry to insert + * @param eventSource Source the event is sent from * @return false if the insert was done without a duplicate warning - * @throws KeyCollisionException thrown if the entry id ({@link BibEntry#getId()}) is already present in the database */ - public synchronized boolean insertEntry(BibEntry entry, boolean isUndo) throws KeyCollisionException { + public synchronized boolean insertEntry(BibEntry entry, EntryEventSource eventSource) + throws KeyCollisionException { Objects.requireNonNull(entry); String id = entry.getId(); @@ -196,22 +197,33 @@ public synchronized boolean insertEntry(BibEntry entry, boolean isUndo) throws K entries.add(entry); entry.registerListener(this); - eventBus.post(new EntryAddedEvent(entry, isUndo)); + eventBus.post(new EntryAddedEvent(entry, eventSource)); return duplicationChecker.checkForDuplicateKeyAndAdd(null, entry.getCiteKeyOptional().orElse(null)); } /** * Removes the given entry. * The Entry is removed based on the id {@link BibEntry#id} + * @param toBeDeleted Entry to delete */ public synchronized void removeEntry(BibEntry toBeDeleted) { + removeEntry(toBeDeleted, EntryEventSource.LOCAL); + } + + /** + * Removes the given entry. + * The Entry is removed based on the id {@link BibEntry#id} + * @param toBeDeleted Entry to delete + * @param eventSource Source the event is sent from + */ + public synchronized void removeEntry(BibEntry toBeDeleted, EntryEventSource eventSource) { Objects.requireNonNull(toBeDeleted); boolean anyRemoved = entries.removeIf(entry -> entry.getId().equals(toBeDeleted.getId())); if (anyRemoved) { internalIDs.remove(toBeDeleted.getId()); toBeDeleted.getCiteKeyOptional().ifPresent(duplicationChecker::removeKeyFromSet); - eventBus.post(new EntryRemovedEvent(toBeDeleted)); + eventBus.post(new EntryRemovedEvent(toBeDeleted, eventSource)); } } @@ -568,6 +580,14 @@ public void registerListener(Object listener) { this.eventBus.register(listener); } + /** + * Unregisters an listener object. + * @param listener listener (subscriber) to remove + */ + public void unregisterListener(Object listener) { + this.eventBus.unregister(listener); + } + @Subscribe private void relayEntryChangeEvent(FieldChangedEvent event) { eventBus.post(event); diff --git a/src/main/java/net/sf/jabref/model/database/DatabaseLocation.java b/src/main/java/net/sf/jabref/model/database/DatabaseLocation.java new file mode 100644 index 000000000000..0c0fa855c7af --- /dev/null +++ b/src/main/java/net/sf/jabref/model/database/DatabaseLocation.java @@ -0,0 +1,11 @@ +package net.sf.jabref.model.database; + +import net.sf.jabref.BibDatabaseContext; + +/** + * This enum represents the location for {@link BibDatabaseContext}. + */ +public enum DatabaseLocation { + LOCAL, + SHARED; +} diff --git a/src/main/java/net/sf/jabref/model/entry/BibEntry.java b/src/main/java/net/sf/jabref/model/entry/BibEntry.java index b646026573b4..3a024f356d09 100644 --- a/src/main/java/net/sf/jabref/model/entry/BibEntry.java +++ b/src/main/java/net/sf/jabref/model/entry/BibEntry.java @@ -34,6 +34,7 @@ import java.util.Set; import java.util.TreeSet; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.model.FieldChange; import net.sf.jabref.model.database.BibDatabase; import net.sf.jabref.model.event.FieldChangedEvent; @@ -52,6 +53,9 @@ public class BibEntry implements Cloneable { public static final String DEFAULT_TYPE = "misc"; private String id; + + private final SharedBibEntryData sharedBibEntryData; + private String type; private Map fields = new HashMap<>(); /* @@ -104,6 +108,7 @@ public BibEntry(String id, String type) { this.id = id; setType(type); + this.sharedBibEntryData = new SharedBibEntryData(); } /** @@ -166,7 +171,7 @@ public String getType() { /** * Sets this entry's type. */ - public void setType(String type) { + public void setType(String type, EntryEventSource eventSource) { String newType; if (Strings.isNullOrEmpty(type)) { newType = DEFAULT_TYPE; @@ -179,7 +184,14 @@ public void setType(String type) { // sets off a change in database sorting etc. this.type = newType.toLowerCase(Locale.ENGLISH); changed = true; - eventBus.post(new FieldChangedEvent(this, TYPE_HEADER, newType)); + eventBus.post(new FieldChangedEvent(this, TYPE_HEADER, newType, eventSource)); + } + + /** + * Sets this entry's type. + */ + public void setType(String type) { + setType(type, EntryEventSource.LOCAL); } /** @@ -345,10 +357,11 @@ public void setField(Map fields) { /** * Set a field, and notify listeners about the change. - * @param name The field to set. - * @param value The value to set. + * @param name The field to set + * @param value The value to set + * @param eventSource Source the event is sent from */ - public Optional setField(String name, String value) { + public Optional setField(String name, String value, EntryEventSource eventSource) { Objects.requireNonNull(name, "field name must not be null"); Objects.requireNonNull(value, "field value must not be null"); @@ -373,10 +386,27 @@ public Optional setField(String name, String value) { fieldsAsWords.remove(fieldName); FieldChange change = new FieldChange(this, fieldName, oldValue, value); - eventBus.post(new FieldChangedEvent(change)); + eventBus.post(new FieldChangedEvent(change, eventSource)); return Optional.of(change); } + public Optional setField(String name, Optional value, EntryEventSource eventSource) { + if (value.isPresent()) { + return setField(name, value.get(), eventSource); + } + return Optional.empty(); + } + + /** + * Set a field, and notify listeners about the change. + * + * @param name The field to set. + * @param value The value to set. + */ + public Optional setField(String name, String value) { + return setField(name, value, EntryEventSource.LOCAL); + } + /** * Remove the mapping for the field name, and notify listeners about * the change. @@ -384,6 +414,17 @@ public Optional setField(String name, String value) { * @param name The field to clear. */ public Optional clearField(String name) { + return clearField(name, EntryEventSource.LOCAL); + } + + /** + * Remove the mapping for the field name, and notify listeners about + * the change including the {@link EntryEventSource}. + * + * @param name The field to clear. + * @param eventSource the source a new {@link FieldChangedEvent} should be posten from. + */ + public Optional clearField(String name, EntryEventSource eventSource) { String fieldName = toLowerCase(name); if (BibEntry.ID_FIELD.equals(fieldName)) { @@ -400,7 +441,7 @@ public Optional clearField(String name) { fields.remove(fieldName); fieldsAsWords.remove(fieldName); FieldChange change = new FieldChange(this, fieldName, oldValue.get(), null); - eventBus.post(new FieldChangedEvent(change)); + eventBus.post(new FieldChangedEvent(change, eventSource)); return Optional.of(change); } @@ -600,6 +641,10 @@ public Map getFieldMap() { return fields; } + public SharedBibEntryData getSharedBibEntryData() { + return sharedBibEntryData; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/net/sf/jabref/model/entry/SharedBibEntryData.java b/src/main/java/net/sf/jabref/model/entry/SharedBibEntryData.java new file mode 100644 index 000000000000..e9b298427eec --- /dev/null +++ b/src/main/java/net/sf/jabref/model/entry/SharedBibEntryData.java @@ -0,0 +1,37 @@ +package net.sf.jabref.model.entry; + +/** + * Stores all informations needed to manage entries on a shared (SQL) database. + */ +public class SharedBibEntryData { + + // This id is set by the remote database system (DBS). + // It has to be unique on remote DBS for all connected JabRef instances. + // The old id above does not satisfy this requirement. + private int sharedID; + + // Needed for version controlling if used on shared database + private int version; + + public SharedBibEntryData() { + this.sharedID = -1; + this.version = 1; + } + + public int getSharedID() { + return sharedID; + } + + public void setSharedID(int sharedID) { + this.sharedID = sharedID; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + +} diff --git a/src/main/java/net/sf/jabref/model/event/EntryAddedEvent.java b/src/main/java/net/sf/jabref/model/event/EntryAddedEvent.java index 4332e37c4d4b..09c7febf8bb0 100644 --- a/src/main/java/net/sf/jabref/model/event/EntryAddedEvent.java +++ b/src/main/java/net/sf/jabref/model/event/EntryAddedEvent.java @@ -15,6 +15,7 @@ */ package net.sf.jabref.model.event; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.model.database.BibDatabase; import net.sf.jabref.model.entry.BibEntry; @@ -23,29 +24,18 @@ */ public class EntryAddedEvent extends EntryEvent { - /** - * flag if the addition is the undo of a deletion/cut - */ - private boolean isUndo; - /** * @param bibEntry the entry which has been added */ public EntryAddedEvent(BibEntry bibEntry) { super(bibEntry); - this.isUndo = false; } /** - * @param bibEntry the entry which has been added - * @param isUndo flag if the addition is the undo of a deletion/cut + * @param bibEntry BibEntry object which has been added. + * @param location Location affected by this event */ - public EntryAddedEvent(BibEntry bibEntry, boolean isUndo) { - super(bibEntry); - this.isUndo = isUndo; - } - - public boolean isUndo() { - return isUndo; + public EntryAddedEvent(BibEntry bibEntry, EntryEventSource location) { + super(bibEntry, location); } } diff --git a/src/main/java/net/sf/jabref/model/event/EntryChangedEvent.java b/src/main/java/net/sf/jabref/model/event/EntryChangedEvent.java index 62e82760d1c6..885d73794521 100644 --- a/src/main/java/net/sf/jabref/model/event/EntryChangedEvent.java +++ b/src/main/java/net/sf/jabref/model/event/EntryChangedEvent.java @@ -1,5 +1,6 @@ package net.sf.jabref.model.event; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.model.entry.BibEntry; /** @@ -15,4 +16,12 @@ public EntryChangedEvent(BibEntry bibEntry) { super(bibEntry); } + /** + * @param bibEntry BibEntry object the changes were applied on. + * @param location Location affected by this event + */ + public EntryChangedEvent(BibEntry bibEntry, EntryEventSource location) { + super(bibEntry, location); + } + } diff --git a/src/main/java/net/sf/jabref/model/event/EntryEvent.java b/src/main/java/net/sf/jabref/model/event/EntryEvent.java index e82959014dd1..89776da94281 100644 --- a/src/main/java/net/sf/jabref/model/event/EntryEvent.java +++ b/src/main/java/net/sf/jabref/model/event/EntryEvent.java @@ -1,5 +1,6 @@ package net.sf.jabref.model.event; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.model.entry.BibEntry; /** @@ -9,15 +10,30 @@ public abstract class EntryEvent { private final BibEntry bibEntry; + private final EntryEventSource location; + /** - * @param bibEntry BibEntry object which is involved in this event + * @param bibEntry BibEntry object which is involved in this event */ public EntryEvent(BibEntry bibEntry) { + this(bibEntry, EntryEventSource.LOCAL); + } + + /** + * @param bibEntry BibEntry object which is involved in this event + * @param location Location affected by this event + */ + public EntryEvent(BibEntry bibEntry, EntryEventSource location) { this.bibEntry = bibEntry; + this.location = location; } public BibEntry getBibEntry() { return this.bibEntry; } + + public EntryEventSource getEntryEventSource() { + return this.location; + } } diff --git a/src/main/java/net/sf/jabref/model/event/EntryRemovedEvent.java b/src/main/java/net/sf/jabref/model/event/EntryRemovedEvent.java index ec6a375557a2..1a0e9d932ee4 100644 --- a/src/main/java/net/sf/jabref/model/event/EntryRemovedEvent.java +++ b/src/main/java/net/sf/jabref/model/event/EntryRemovedEvent.java @@ -1,5 +1,6 @@ package net.sf.jabref.model.event; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.model.entry.BibEntry; /** @@ -16,4 +17,12 @@ public EntryRemovedEvent(BibEntry bibEntry) { super(bibEntry); } + /** + * @param bibEntry BibEntry object which has been removed. + * @param location Location affected by this event + */ + public EntryRemovedEvent(BibEntry bibEntry, EntryEventSource location) { + super(bibEntry, location); + } + } diff --git a/src/main/java/net/sf/jabref/model/event/FieldChangedEvent.java b/src/main/java/net/sf/jabref/model/event/FieldChangedEvent.java index f634d77a33dd..6984dfa13e6d 100644 --- a/src/main/java/net/sf/jabref/model/event/FieldChangedEvent.java +++ b/src/main/java/net/sf/jabref/model/event/FieldChangedEvent.java @@ -1,5 +1,6 @@ package net.sf.jabref.model.event; +import net.sf.jabref.event.source.EntryEventSource; import net.sf.jabref.model.FieldChange; import net.sf.jabref.model.entry.BibEntry; @@ -11,6 +12,18 @@ public class FieldChangedEvent extends EntryChangedEvent { private final String fieldName; private final String newValue; + /** + * @param bibEntry Affected BibEntry object + * @param fieldName Name of field which has been changed + * @param newValue new field value + * @param location location Location affected by this event + */ + public FieldChangedEvent(BibEntry bibEntry, String fieldName, String newValue, EntryEventSource location) { + super(bibEntry, location); + this.fieldName = fieldName; + this.newValue = newValue; + } + /** * @param bibEntry Affected BibEntry object * @param fieldName Name of field which has been changed @@ -22,8 +35,20 @@ public FieldChangedEvent(BibEntry bibEntry, String fieldName, String newValue) { this.newValue = newValue; } + /** + * @param bibEntry Affected BibEntry object + * @param fieldName Name of field which has been changed + * @param newValue new field value + * @param location location Location affected by this event + */ + public FieldChangedEvent(FieldChange fieldChange, EntryEventSource location) { + super(fieldChange.getEntry(), location); + this.fieldName = fieldChange.getField(); + this.newValue = fieldChange.getNewValue(); + } + public FieldChangedEvent(FieldChange fieldChange) { - this(fieldChange.getEntry(), fieldChange.getField(), fieldChange.getNewValue()); + this(fieldChange, EntryEventSource.LOCAL); } public String getFieldName() { diff --git a/src/main/java/net/sf/jabref/preferences/JabRefPreferences.java b/src/main/java/net/sf/jabref/preferences/JabRefPreferences.java index c3889b7f65d9..7b8c1f2b2430 100644 --- a/src/main/java/net/sf/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/net/sf/jabref/preferences/JabRefPreferences.java @@ -970,6 +970,10 @@ public String get(String key) { return prefs.get(key, (String) defaults.get(key)); } + public Optional getAsOptional(String key) { + return Optional.ofNullable(prefs.get(key, (String) defaults.get(key))); + } + public String get(String key, String def) { return prefs.get(key, def); } diff --git a/src/main/java/net/sf/jabref/shared/DBMSConnectionProperties.java b/src/main/java/net/sf/jabref/shared/DBMSConnectionProperties.java new file mode 100644 index 000000000000..d85283c5a522 --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/DBMSConnectionProperties.java @@ -0,0 +1,78 @@ +package net.sf.jabref.shared; + +/** + * Keeps all essential data for establishing a new connection to a DBMS using {@link DBMSConnector}. + */ +public class DBMSConnectionProperties { + + private DBMSType type; + private String host; + private int port; + private String database; + private String user; + private String password; + + + public DBMSConnectionProperties() { + // no data + } + + public DBMSConnectionProperties(DBMSType type, String host, int port, String database, String user, + String password) { + this.type = type; + this.host = host; + this.port = port; + this.database = database; + this.user = user; + this.password = password; + } + + public DBMSType getType() { + return type; + } + + public void setType(DBMSType type) { + this.type = type; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getDatabase() { + return database; + } + + public void setDatabase(String database) { + this.database = database; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + +} diff --git a/src/main/java/net/sf/jabref/shared/DBMSConnector.java b/src/main/java/net/sf/jabref/shared/DBMSConnector.java new file mode 100644 index 000000000000..5922ccfdf040 --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/DBMSConnector.java @@ -0,0 +1,84 @@ +/* Copyright (C) 2003-2016 JabRef contributors. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +package net.sf.jabref.shared; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Set; + +import net.sf.jabref.logic.l10n.Localization; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Used to establish connections between JabRef and database systems like MySQL, PostgreSQL and Oracle. + */ +public class DBMSConnector { + + private static final Log LOGGER = LogFactory.getLog(DBMSConnector.class); + + + /** + * Determines the suitable driver and retrieves a working SQL Connection in normal case. + * + * @param dbmsType Enum entry of {@link DBMSType} which determines the driver + * @param host Hostname, Domain or IP address + * @param port Port number the server is listening on + * @param database An already existent database name. + * @param user Username + * @param password Password + * @return + * @throws ClassNotFoundException Thrown if no suitable drivers were found + * @throws SQLException Thrown if connection has failed + */ + public static Connection getNewConnection(DBMSConnectionProperties properties) + throws ClassNotFoundException, SQLException { + + try { + DriverManager.setLoginTimeout(3); + return DriverManager.getConnection( + properties.getType().getUrl(properties.getHost(), properties.getPort(), properties.getDatabase()), + properties.getUser(), properties.getPassword()); + } catch (SQLException e) { + // Some systems like PostgreSQL retrieves 0 to every exception. + // Therefore a stable error determination is not possible. + LOGGER.error("Could not connect to database: " + e.getMessage() + " - Error code: " + e.getErrorCode()); + + throw e; + } + } + + /** + * Returns a Set of {@link DBMSType} which is supported by available drivers. + */ + public static Set getAvailableDBMSTypes() { + Set dbmsTypes = new HashSet<>(); + + for (DBMSType dbms : DBMSType.values()) { + try { + Class.forName(dbms.getDriverClassPath()); + dbmsTypes.add(dbms); + } catch (ClassNotFoundException e) { + // In case that the driver is not available do not perform tests for this system. + LOGGER.info(Localization.lang("%0 driver not available.", dbms.toString())); + } + } + return dbmsTypes; + } +} diff --git a/src/main/java/net/sf/jabref/shared/DBMSProcessor.java b/src/main/java/net/sf/jabref/shared/DBMSProcessor.java new file mode 100644 index 000000000000..cb9bc002af0b --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/DBMSProcessor.java @@ -0,0 +1,513 @@ +/* Copyright (C) 2003-2016 JabRef contributors. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +package net.sf.jabref.shared; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import net.sf.jabref.event.source.EntryEventSource; +import net.sf.jabref.logic.l10n.Localization; +import net.sf.jabref.model.entry.BibEntry; +import net.sf.jabref.shared.exception.OfflineLockException; +import net.sf.jabref.shared.exception.SharedEntryNotPresentException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Processes all incoming or outgoing bib data to external SQL Database and manages its structure. + */ +public abstract class DBMSProcessor { + + protected static final Log LOGGER = LogFactory.getLog(DBMSConnector.class); + + protected final Connection connection; + + /** + * @param connection Working SQL connection + * @param dbmsType Instance of {@link DBMSType} + */ + public DBMSProcessor(Connection connection) { + this.connection = connection; + } + + /** + * Scans the database for required tables. + * @return true if the structure matches the requirements, false if not. + * @throws SQLException + */ + public boolean checkBaseIntegrity() throws SQLException { + List requiredTables = new ArrayList<>(Arrays.asList("ENTRY", "FIELD", "METADATA")); // the list should be dynamic + DatabaseMetaData databaseMetaData = connection.getMetaData(); + + // ...getTables(null, ...): no restrictions + try (ResultSet databaseMetaDataResultSet = databaseMetaData.getTables(null, null, null, null)) { + while (databaseMetaDataResultSet.next()) { + String tableName = databaseMetaDataResultSet.getString("TABLE_NAME").toUpperCase(); + requiredTables.remove(tableName); // Remove matching tables to check requiredTables for emptiness + } + return requiredTables.isEmpty(); + } + } + + + /** + * Creates and sets up the needed tables and columns according to the database type and + * performs a check whether the needed tables are present. + * + * @throws SQLException + */ + public void setUpSharedDatabase() throws SQLException { + setUp(); + + if (!checkBaseIntegrity()) { + // can only happen with users direct intervention on shared database + LOGGER.error(Localization.lang("Corrupt_shared_database_structure.")); + } + } + + /** + * Creates and sets up the needed tables and columns according to the database type. + * @throws SQLException + */ + protected abstract void setUp() throws SQLException; + + /** + * Escapes parts of SQL expressions like table or field name to match the conventions + * of the database system using the current dbmsType. + * @param expression Table or field name + * @return Correctly escaped expression + */ + public abstract String escape(String expression); + + + /** + * Inserts the given bibEntry into shared database. + * @param bibEntry {@link BibEntry} to be inserted + */ + public void insertEntry(BibEntry bibEntry) { + + + try { + // Check if already exists + int sharedID = bibEntry.getSharedBibEntryData().getSharedID(); + if (sharedID != -1) { + StringBuilder selectQuery = new StringBuilder() + .append("SELECT * FROM ") + .append(escape("ENTRY")) + .append(" WHERE ") + .append(escape("SHARED_ID")) + .append(" = ?"); + + try (PreparedStatement preparedSelectStatement = connection.prepareStatement(selectQuery.toString())) { + preparedSelectStatement.setInt(1, sharedID); + try (ResultSet resultSet = preparedSelectStatement.executeQuery()) { + if (resultSet.next()) { + return; + } + } + } + } + + // Inserting into ENTRY table + StringBuilder insertIntoEntryQuery = new StringBuilder() + .append("INSERT INTO ") + .append(escape("ENTRY")) + .append("(") + .append(escape("TYPE")) + .append(") VALUES(?)"); + + // This is the only method to get generated keys which is accepted by MySQL, PostgreSQL and Oracle. + try (PreparedStatement preparedEntryStatement = connection.prepareStatement(insertIntoEntryQuery.toString(), + new String[] {"SHARED_ID"})) { + + preparedEntryStatement.setString(1, bibEntry.getType()); + preparedEntryStatement.executeUpdate(); + + try (ResultSet generatedKeys = preparedEntryStatement.getGeneratedKeys()) { + if (generatedKeys.next()) { + bibEntry.getSharedBibEntryData().setSharedID(generatedKeys.getInt(1)); // set generated ID locally + } + } + + // Inserting into FIELD table + for (String fieldName : bibEntry.getFieldNames()) { + StringBuilder insertFieldQuery = new StringBuilder() + .append("INSERT INTO ") + .append(escape("FIELD")) + .append("(") + .append(escape("ENTRY_SHARED_ID")) + .append(", ") + .append(escape("NAME")) + .append(", ") + .append(escape("VALUE")) + .append(") VALUES(?, ?, ?)"); + + try (PreparedStatement preparedFieldStatement = connection.prepareStatement(insertFieldQuery.toString())) { + // columnIndex starts with 1 + preparedFieldStatement.setInt(1, bibEntry.getSharedBibEntryData().getSharedID()); + preparedFieldStatement.setString(2, fieldName); + preparedFieldStatement.setString(3, bibEntry.getFieldOptional(fieldName).get()); + preparedFieldStatement.executeUpdate(); + } + } + } + } catch (SQLException e) { + LOGGER.error("SQL Error: ", e); + } + } + + /** + * Updates the whole {@link BibEntry} on shared database. + * + * @param localBibEntry {@link BibEntry} affected by changes + * @throws SQLException + */ + public void updateEntry(BibEntry localBibEntry) throws OfflineLockException, SharedEntryNotPresentException, SQLException { + connection.setAutoCommit(false); // disable auto commit due to transaction + + try { + Optional sharedEntryOptional = getSharedEntry(localBibEntry.getSharedBibEntryData().getSharedID()); + + if (!sharedEntryOptional.isPresent()) { + throw new SharedEntryNotPresentException(localBibEntry); + } + + BibEntry sharedBibEntry = sharedEntryOptional.get(); + + // remove shared fields which do not exist locally + removeSharedFieldsByDifference(localBibEntry, sharedBibEntry); + + // update only if local version is higher or the entries are equal + if ((localBibEntry.getSharedBibEntryData().getVersion() >= sharedBibEntry.getSharedBibEntryData() + .getVersion()) || localBibEntry.equals(sharedBibEntry)) { + + insertOrUpdateFields(localBibEntry); + + // updating entry type + StringBuilder updateEntryTypeQuery = new StringBuilder() + .append("UPDATE ") + .append(escape("ENTRY")) + .append(" SET ") + .append(escape("TYPE")) + .append(" = ?, ") + .append(escape("VERSION")) + .append(" = ") + .append(escape("VERSION")) + .append(" + 1 WHERE ") + .append(escape("SHARED_ID")) + .append(" = ?"); + + try (PreparedStatement preparedUpdateEntryTypeStatement = connection.prepareStatement(updateEntryTypeQuery.toString())) { + preparedUpdateEntryTypeStatement.setString(1, localBibEntry.getType()); + preparedUpdateEntryTypeStatement.setInt(2, localBibEntry.getSharedBibEntryData().getSharedID()); + preparedUpdateEntryTypeStatement.executeUpdate(); + } + + connection.commit(); // apply all changes in current transaction + + } else { + throw new OfflineLockException(localBibEntry, sharedBibEntry); + } + } catch (SQLException e) { + LOGGER.error("SQL Error: ", e); + connection.rollback(); // undo changes made in current transaction + } finally { + connection.setAutoCommit(true); // enable auto commit mode again + } + } + + /** + * Helping method. Removes shared fields which do not exist locally + */ + private void removeSharedFieldsByDifference(BibEntry localBibEntry, BibEntry sharedBibEntry) throws SQLException { + Set nullFields = new HashSet<>(sharedBibEntry.getFieldNames()); + nullFields.removeAll(localBibEntry.getFieldNames()); + for (String nullField : nullFields) { + StringBuilder deleteFieldQuery = new StringBuilder() + .append("DELETE FROM ") + .append(escape("FIELD")) + .append(" WHERE ") + .append(escape("NAME")) + .append(" = ? AND ") + .append(escape("ENTRY_SHARED_ID")) + .append(" = ?"); + + try (PreparedStatement preparedDeleteFieldStatement = connection + .prepareStatement(deleteFieldQuery.toString())) { + preparedDeleteFieldStatement.setString(1, nullField); + preparedDeleteFieldStatement.setInt(2, localBibEntry.getSharedBibEntryData().getSharedID()); + preparedDeleteFieldStatement.executeUpdate(); + } + } + } + + /** + * Helping method. Inserts a key-value pair into FIELD table for every field if not existing. Otherwise only an update is performed. + */ + private void insertOrUpdateFields(BibEntry localBibEntry) throws SQLException { + for (String fieldName : localBibEntry.getFieldNames()) { + // avoiding to use deprecated BibEntry.getField() method. null values are accepted by PreparedStatement! + Optional valueOptional = localBibEntry.getFieldOptional(fieldName); + String value = null; + if (valueOptional.isPresent()) { + value = valueOptional.get(); + } + + StringBuilder selectFieldQuery = new StringBuilder() + .append("SELECT * FROM ") + .append(escape("FIELD")) + .append(" WHERE ") + .append(escape("NAME")) + .append(" = ? AND ") + .append(escape("ENTRY_SHARED_ID")) + .append(" = ?"); + + try (PreparedStatement preparedSelectFieldStatement = connection + .prepareStatement(selectFieldQuery.toString())) { + preparedSelectFieldStatement.setString(1, fieldName); + preparedSelectFieldStatement.setInt(2, localBibEntry.getSharedBibEntryData().getSharedID()); + + try (ResultSet selectFieldResultSet = preparedSelectFieldStatement.executeQuery()) { + if (selectFieldResultSet.next()) { // check if field already exists + StringBuilder updateFieldQuery = new StringBuilder() + .append("UPDATE ") + .append(escape("FIELD")) + .append(" SET ") + .append(escape("VALUE")) + .append(" = ? WHERE ") + .append(escape("NAME")) + .append(" = ? AND ") + .append(escape("ENTRY_SHARED_ID")) + .append(" = ?"); + + try (PreparedStatement preparedUpdateFieldStatement = connection + .prepareStatement(updateFieldQuery.toString())) { + preparedUpdateFieldStatement.setString(1, value); + preparedUpdateFieldStatement.setString(2, fieldName); + preparedUpdateFieldStatement.setInt(3, localBibEntry.getSharedBibEntryData().getSharedID()); + preparedUpdateFieldStatement.executeUpdate(); + } + } else { + StringBuilder insertFieldQuery = new StringBuilder() + .append("INSERT INTO ") + .append(escape("FIELD")) + .append("(") + .append(escape("ENTRY_SHARED_ID")) + .append(", ") + .append(escape("NAME")) + .append(", ") + .append(escape("VALUE")) + .append(") VALUES(?, ?, ?)"); + + try (PreparedStatement preparedFieldStatement = connection + .prepareStatement(insertFieldQuery.toString())) { + preparedFieldStatement.setInt(1, localBibEntry.getSharedBibEntryData().getSharedID()); + preparedFieldStatement.setString(2, fieldName); + preparedFieldStatement.setString(3, value); + preparedFieldStatement.executeUpdate(); + } + } + } + } + } + } + + /** + * Removes the shared bibEntry. + * @param bibEntry {@link BibEntry} to be deleted + */ + public void removeEntry(BibEntry bibEntry) { + StringBuilder query = new StringBuilder() + .append("DELETE FROM ") + .append(escape("ENTRY")) + .append(" WHERE ") + .append(escape("SHARED_ID")) + .append(" = ?"); + + try (PreparedStatement preparedStatement = connection.prepareStatement(query.toString())) { + preparedStatement.setInt(1, bibEntry.getSharedBibEntryData().getSharedID()); + preparedStatement.executeUpdate(); + } catch (SQLException e) { + LOGGER.error("SQL Error: ", e); + } + + } + + /** + * @param sharedID Entry ID + * @return instance of {@link BibEntry} + */ + public Optional getSharedEntry(int sharedID) { + List sharedEntries = getSharedEntryList(sharedID); + if (!sharedEntries.isEmpty()) { + return Optional.of(sharedEntries.get(0)); + } + return Optional.empty(); + } + + public List getSharedEntries() { + return getSharedEntryList(0); + } + + /** + * @param sharedID Entry ID. If 0, all entries are going to be fetched. + * @return List of {@link BibEntry} instances + */ + private List getSharedEntryList(int sharedID) { + List sharedEntries = new ArrayList<>(); + + StringBuilder selectEntryQuery = new StringBuilder(); + selectEntryQuery.append("SELECT * FROM "); + selectEntryQuery.append(escape("ENTRY")); + + if (sharedID != 0) { + selectEntryQuery.append(" WHERE "); + selectEntryQuery.append(escape("SHARED_ID")); + selectEntryQuery.append(" = "); + selectEntryQuery.append(sharedID); + } + + selectEntryQuery.append(" ORDER BY "); + selectEntryQuery.append(escape("SHARED_ID")); + + try (ResultSet selectEntryResultSet = connection.createStatement().executeQuery(selectEntryQuery.toString())) { + while (selectEntryResultSet.next()) { + BibEntry bibEntry = new BibEntry(); + // setting the base attributes once + bibEntry.getSharedBibEntryData().setSharedID(selectEntryResultSet.getInt("SHARED_ID")); + bibEntry.setType(selectEntryResultSet.getString("TYPE")); + bibEntry.getSharedBibEntryData().setVersion(selectEntryResultSet.getInt("VERSION")); + + StringBuilder selectFieldQuery = new StringBuilder() + .append("SELECT * FROM ") + .append(escape("FIELD")) + .append(" WHERE ") + .append(escape("ENTRY_SHARED_ID")) + .append(" = ?"); + + try (PreparedStatement preparedSelectFieldStatement = connection.prepareStatement(selectFieldQuery.toString())) { + preparedSelectFieldStatement.setInt(1, selectEntryResultSet.getInt("SHARED_ID")); + try (ResultSet selectFieldResultSet = preparedSelectFieldStatement.executeQuery()) { + while (selectFieldResultSet.next()) { + bibEntry.setField(selectFieldResultSet.getString("NAME"), + Optional.ofNullable(selectFieldResultSet.getString("VALUE")), EntryEventSource.SHARED); + } + } + } + sharedEntries.add(bibEntry); + } + } catch (SQLException e) { + LOGGER.error("SQL Error", e); + } + + return sharedEntries; + } + + /** + * Retrieves a mapping between the columns SHARED_ID and VERSION. + */ + public Map getSharedIDVersionMapping() { + Map sharedIDVersionMapping = new HashMap<>(); + StringBuilder selectEntryQuery = new StringBuilder() + .append("SELECT * FROM ") + .append(escape("ENTRY")) + .append(" ORDER BY ") + .append(escape("SHARED_ID")); + + try (ResultSet selectEntryResultSet = connection.createStatement().executeQuery(selectEntryQuery.toString())) { + while (selectEntryResultSet.next()) { + sharedIDVersionMapping.put(selectEntryResultSet.getInt("SHARED_ID"), selectEntryResultSet.getInt("VERSION")); + } + } catch (SQLException e) { + LOGGER.error("SQL Error", e); + } + + return sharedIDVersionMapping; + } + + /** + * Fetches and returns all shared meta data. + */ + public Map getSharedMetaData() { + Map data = new HashMap<>(); + + try (ResultSet resultSet = connection.createStatement().executeQuery("SELECT * FROM " + escape("METADATA"))) { + while(resultSet.next()) { + data.put(resultSet.getString("KEY"), resultSet.getString("VALUE")); + } + } catch (SQLException e) { + LOGGER.error("SQL Error", e); + } + + return data; + } + + /** + * Clears and sets all shared meta data. + * @param metaData JabRef meta data. + * @throws SQLException + */ + public void setSharedMetaData(Map data) throws SQLException { + connection.createStatement().executeUpdate("TRUNCATE TABLE " + escape("METADATA")); // delete data all data from table + + for (Map.Entry metaEntry : data.entrySet()) { + + StringBuilder query = new StringBuilder() + .append("INSERT INTO ") + .append(escape("METADATA")) + .append("(") + .append(escape("KEY")) + .append(", ") + .append(escape("VALUE")) + .append(") VALUES(?, ?)"); + + try (PreparedStatement preparedStatement = connection.prepareStatement(query.toString())) { + preparedStatement.setString(1, metaEntry.getKey()); + preparedStatement.setString(2, metaEntry.getValue()); + preparedStatement.executeUpdate(); + } catch (SQLException e) { + LOGGER.error("SQL Error: ", e); + } + } + } + + /** + * Returns a new instance of the abstract type {@link DBMSProcessor} + */ + public static DBMSProcessor getProcessorInstance(Connection connection, DBMSType type) { + if (type == DBMSType.MYSQL) { + return new MySQLProcessor(connection); + } else if (type == DBMSType.POSTGRESQL) { + return new PostgreSQLProcessor(connection); + } else if (type == DBMSType.ORACLE) { + return new OracleProcessor(connection); + } + return null; // can never happen except new types were added without updating this method. + } + +} diff --git a/src/main/java/net/sf/jabref/shared/DBMSSynchronizer.java b/src/main/java/net/sf/jabref/shared/DBMSSynchronizer.java new file mode 100644 index 000000000000..16a12820c540 --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/DBMSSynchronizer.java @@ -0,0 +1,370 @@ +/* Copyright (C) 2003-2016 JabRef contributors. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +package net.sf.jabref.shared; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import net.sf.jabref.BibDatabaseContext; +import net.sf.jabref.MetaData; +import net.sf.jabref.event.MetaDataChangedEvent; +import net.sf.jabref.event.source.EntryEventSource; +import net.sf.jabref.importer.fileformat.ParseException; +import net.sf.jabref.logic.exporter.BibDatabaseWriter; +import net.sf.jabref.logic.l10n.Localization; +import net.sf.jabref.model.database.BibDatabase; +import net.sf.jabref.model.entry.BibEntry; +import net.sf.jabref.model.event.EntryAddedEvent; +import net.sf.jabref.model.event.EntryEvent; +import net.sf.jabref.model.event.EntryRemovedEvent; +import net.sf.jabref.model.event.FieldChangedEvent; +import net.sf.jabref.shared.event.ConnectionLostEvent; +import net.sf.jabref.shared.event.SharedEntryNotPresentEvent; +import net.sf.jabref.shared.event.UpdateRefusedEvent; +import net.sf.jabref.shared.exception.OfflineLockException; +import net.sf.jabref.shared.exception.SharedEntryNotPresentException; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Synchronizes the shared or local databases with their opposite side. + * Local changes are pushed by {@link EntryEvent} using Google's Guava EventBus. + */ +public class DBMSSynchronizer { + + private static final Log LOGGER = LogFactory.getLog(DBMSConnector.class); + + private DBMSProcessor dbmsProcessor; + private DBMSType dbmsType; + private String dbName; + private final BibDatabaseContext bibDatabaseContext; + private MetaData metaData; + private final BibDatabase bibDatabase; + private final EventBus eventBus; + private Connection currentConnection; + + + public DBMSSynchronizer(BibDatabaseContext bibDatabaseContext) { + this.bibDatabaseContext = bibDatabaseContext; + this.bibDatabase = bibDatabaseContext.getDatabase(); + this.metaData = bibDatabaseContext.getMetaData(); + this.eventBus = new EventBus(); + } + + /** + * Listening method. Inserts a new {@link BibEntry} into shared database. + * @param event {@link EntryAddedEvent} object + */ + @Subscribe + public void listen(EntryAddedEvent event) { + // While synchronizing the local database (see synchronizeLocalDatabase() below), some EntryEvents may be posted. + // In this case DBSynchronizer should not try to insert the bibEntry entry again (but it would not harm). + if (isEventSourceAccepted(event) && checkCurrentConnection()) { + dbmsProcessor.insertEntry(event.getBibEntry()); + synchronizeLocalMetaData(); + synchronizeLocalDatabase(); // Pull changes for the case that there were some + } + } + + /** + * Listening method. Updates an existing shared {@link BibEntry}. + * @param event {@link FieldChangedEvent} object + */ + @Subscribe + public void listen(FieldChangedEvent event) { + // While synchronizing the local database (see synchronizeLocalDatabase() below), some EntryEvents may be posted. + // In this case DBSynchronizer should not try to update the bibEntry entry again (but it would not harm). + if (isEventSourceAccepted(event) && checkCurrentConnection()) { + synchronizeLocalMetaData(); + BibEntry bibEntry = event.getBibEntry(); + synchronizeSharedEntry(bibEntry); + synchronizeLocalDatabase(); // Pull changes for the case that there were some + } + } + + /** + * Listening method. Deletes the given {@link BibEntry} from shared database. + * @param event {@link EntryRemovedEvent} object + */ + @Subscribe + public void listen(EntryRemovedEvent event) { + // While synchronizing the local database (see synchronizeLocalDatabase() below), some EntryEvents may be posted. + // In this case DBSynchronizer should not try to delete the bibEntry entry again (but it would not harm). + if (isEventSourceAccepted(event) && checkCurrentConnection()) { + dbmsProcessor.removeEntry(event.getBibEntry()); + synchronizeLocalMetaData(); + synchronizeLocalDatabase(); // Pull changes for the case that there where some + } + } + + /** + * Listening method. Synchronizes the shared {@link MetaData} and applies them locally. + * @param event + */ + @Subscribe + public void listen(MetaDataChangedEvent event) { + if (checkCurrentConnection()) { + synchronizeSharedMetaData(event.getMetaData()); + synchronizeLocalDatabase(); + applyMetaData(); + } + } + + /** + * Sets the table structure of shared database if needed and pulls all shared entries + * to the new local database. + * @param bibDatabase Local {@link BibDatabase} + */ + public void initializeDatabases() { + try { + if (!dbmsProcessor.checkBaseIntegrity()) { + LOGGER.info(Localization.lang("Integrity check failed. Fixing...")); + dbmsProcessor.setUpSharedDatabase(); + } + } catch (SQLException e) { + LOGGER.error("SQL Error: ", e); + } + synchronizeLocalMetaData(); + synchronizeLocalDatabase(); + } + + /** + * Synchronizes the local database with shared one. + * Possible update types are removal, update or insert of a {@link BibEntry}. + */ + public void synchronizeLocalDatabase() { + if (!checkCurrentConnection()) { + return; + } + + List localEntries = bibDatabase.getEntries(); + Map idVersionMap = dbmsProcessor.getSharedIDVersionMapping(); + + // remove old entries locally + removeNotSharedEntries(localEntries, idVersionMap.keySet()); + + // compare versions and update local entry if needed + for (Map.Entry idVersionEntry : idVersionMap.entrySet()) { + boolean match = false; + for (BibEntry localEntry : localEntries) { + if (idVersionEntry.getKey() == localEntry.getSharedBibEntryData().getSharedID()) { + match = true; + if (idVersionEntry.getValue() > localEntry.getSharedBibEntryData().getVersion()) { + Optional sharedEntry = dbmsProcessor.getSharedEntry(idVersionEntry.getKey()); + if (sharedEntry.isPresent()) { + // update fields + localEntry.setType(sharedEntry.get().getType(), EntryEventSource.SHARED); + localEntry.getSharedBibEntryData() + .setVersion(sharedEntry.get().getSharedBibEntryData().getVersion()); + for (String field : sharedEntry.get().getFieldNames()) { + localEntry.setField(field, sharedEntry.get().getFieldOptional(field), EntryEventSource.SHARED); + } + + Set redundantLocalEntryFields = localEntry.getFieldNames(); + redundantLocalEntryFields.removeAll(sharedEntry.get().getFieldNames()); + + // remove not existing fields + for (String redundantField : redundantLocalEntryFields) { + localEntry.clearField(redundantField, EntryEventSource.SHARED); + } + } + } + } + } + if (!match) { + Optional bibEntry = dbmsProcessor.getSharedEntry(idVersionEntry.getKey()); + if (bibEntry.isPresent()) { + bibDatabase.insertEntry(bibEntry.get(), EntryEventSource.SHARED); + } + } + } + } + + /** + * Removes all local entries which are not present on shared database. + * + * @param localEntries List of {@link BibEntry} the entries should be removed from + * @param sharedIDs Set of all IDs which are present on shared database + */ + private void removeNotSharedEntries(List localEntries, Set sharedIDs) { + for (int i = 0; i < localEntries.size(); i++) { + BibEntry localEntry = localEntries.get(i); + boolean match = false; + for (int sharedID : sharedIDs) { + if (localEntry.getSharedBibEntryData().getSharedID() == sharedID) { + match = true; + break; + } + } + if (!match) { + bibDatabase.removeEntry(localEntry, EntryEventSource.SHARED); // Should not reach the listeners above. + i--; // due to index shift on localEntries + } + } + } + + /** + * Synchronizes the shared {@link BibEntry} with the local one. + */ + public void synchronizeSharedEntry(BibEntry bibEntry) { + if (!checkCurrentConnection()) { + return; + } + try { + BibDatabaseWriter.applySaveActions(bibEntry, metaData); // perform possibly existing save actions + dbmsProcessor.updateEntry(bibEntry); + } catch (OfflineLockException exception) { + eventBus.post(new UpdateRefusedEvent(bibDatabaseContext, exception.getLocalBibEntry(), exception.getSharedBibEntry())); + } catch (SharedEntryNotPresentException exception) { + eventBus.post(new SharedEntryNotPresentEvent(exception.getNonPresentBibEntry())); + } catch (SQLException e) { + LOGGER.error("SQL Error: ", e); + } + } + + /** + * Synchronizes all meta data locally. + */ + public void synchronizeLocalMetaData() { + if (!checkCurrentConnection()) { + return; + } + + try { + metaData.setData(dbmsProcessor.getSharedMetaData()); + } catch (ParseException e) { + LOGGER.error("Parse error", e); + } + } + + /** + * Synchronizes all shared meta data. + */ + public void synchronizeSharedMetaData(MetaData data) { + if (!checkCurrentConnection()) { + return; + } + try { + dbmsProcessor.setSharedMetaData(data.getAsStringMap()); + } catch (SQLException e) { + LOGGER.error("SQL Error: ", e); + } + } + + /** + * Applies the {@link MetaData} on all local and shared BibEntries. + */ + public void applyMetaData() { + if (!checkCurrentConnection()) { + return; + } + for (BibEntry bibEntry : bibDatabase.getEntries()) { + // synchronize only if changes were present + if (!BibDatabaseWriter.applySaveActions(bibEntry, metaData).isEmpty()) { + try { + dbmsProcessor.updateEntry(bibEntry); + } catch (OfflineLockException exception) { + eventBus.post(new UpdateRefusedEvent(bibDatabaseContext, exception.getLocalBibEntry(), exception.getSharedBibEntry())); + } catch (SharedEntryNotPresentException exception) { + eventBus.post(new SharedEntryNotPresentEvent(exception.getNonPresentBibEntry())); + } catch (SQLException e) { + LOGGER.error("SQL Error: ", e); + } + } + } + } + + /** + * Synchronizes the local BibEntries and applies the fetched MetaData on them. + */ + public void pullChanges() { + if (!checkCurrentConnection()) { + return; + } + + synchronizeLocalDatabase(); + synchronizeLocalMetaData(); + } + + /** + * Checks whether the current SQL connection is valid. + * In case that the connection is not valid a new {@link ConnectionLostEvent} is going to be sent. + * + * @return true if the connection is valid, else false. + */ + public boolean checkCurrentConnection() { + try { + boolean isValid = currentConnection.isValid(0); + if (!isValid) { + eventBus.post(new ConnectionLostEvent(bibDatabaseContext)); + } + return isValid; + + } catch (SQLException e) { + LOGGER.error("SQL Error:", e); + return false; + } + } + + /** + * Checks whether the {@link EntryEventSource} of an {@link EntryEvent} is crucial for this class. + * @param event An {@link EntryEvent} + * @return true if the event is able to trigger operations in {@link DBMSSynchronizer}, else false + */ + public boolean isEventSourceAccepted(EntryEvent event) { + EntryEventSource eventSource = event.getEntryEventSource(); + return ((eventSource == EntryEventSource.LOCAL) || (eventSource == EntryEventSource.UNDO)); + } + + public void openSharedDatabase(Connection connection, DBMSType type, String name) { + this.dbmsType = type; + this.dbName = name; + this.currentConnection = connection; + this.dbmsProcessor = DBMSProcessor.getProcessorInstance(connection, type); + initializeDatabases(); + } + + public void openSharedDatabase(DBMSConnectionProperties properties) throws ClassNotFoundException, SQLException { + openSharedDatabase(DBMSConnector.getNewConnection(properties), properties.getType(), properties.getDatabase()); + } + + public String getDBName() { + return dbName; + } + + public DBMSType getDBType() { + return this.dbmsType; + } + + public DBMSProcessor getDBProcessor() { + return dbmsProcessor; + } + + public void setMetaData(MetaData metaData) { + this.metaData = metaData; + } + + public void registerListener(Object listener) { + eventBus.register(listener); + } +} diff --git a/src/main/java/net/sf/jabref/shared/DBMSType.java b/src/main/java/net/sf/jabref/shared/DBMSType.java new file mode 100644 index 000000000000..7fe4d4fb16cc --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/DBMSType.java @@ -0,0 +1,86 @@ +/* Copyright (C) 2003-2016 JabRef contributors. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +package net.sf.jabref.shared; + +import java.util.Locale; +import java.util.Optional; + +/** + * Enumerates all supported database systems (DBMS) by JabRef. + */ +public enum DBMSType { + + MYSQL( + "MySQL", + "com.mysql.jdbc.Driver", + "jdbc:mysql://%s:%d/%s", 3306), + ORACLE( + "Oracle", + "oracle.jdbc.driver.OracleDriver", + "jdbc:oracle:thin:@%s:%d:%s", 1521), + POSTGRESQL( + "PostgreSQL", + "org.postgresql.Driver", + "jdbc:postgresql://%s:%d/%s", 5432); + + private final String type; + private final String driverPath; + private final String urlPattern; + private final int defaultPort; + + + private DBMSType(String type, String driverPath, String urlPattern, int defaultPort) { + this.type = type; + this.driverPath = driverPath; + this.urlPattern = urlPattern; + this.defaultPort = defaultPort; + } + + @Override + public String toString() { + return this.type; + } + + /** + * @return Java Class path for establishing JDBC connection. + */ + public String getDriverClassPath() throws Error { + return this.driverPath; + } + + /** + * @return prepared connection URL for appropriate system. + */ + public String getUrl(String host, int port, String database) { + return String.format(urlPattern, host, port, database); + } + + /** + * Retrieves the port number dependent on the type of the database system. + */ + public int getDefaultPort() { + return this.defaultPort; + } + + public static Optional fromString(String typeName) { + try { + return Optional.of(Enum.valueOf(DBMSType.class, typeName.toUpperCase(Locale.ENGLISH))); + } catch (IllegalArgumentException exception) { + return Optional.empty(); + } + } + +} diff --git a/src/main/java/net/sf/jabref/shared/MySQLProcessor.java b/src/main/java/net/sf/jabref/shared/MySQLProcessor.java new file mode 100644 index 000000000000..303d0c96a019 --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/MySQLProcessor.java @@ -0,0 +1,47 @@ +package net.sf.jabref.shared; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Processes all incoming or outgoing bib data to MySQL Database and manages its structure. + */ +public class MySQLProcessor extends DBMSProcessor { + + /** + * @param connection Working SQL connection + * @param dbmsType Instance of {@link DBMSType} + */ + public MySQLProcessor(Connection connection) { + super(connection); + } + + /** + * Creates and sets up the needed tables and columns according to the database type. + * @throws SQLException + */ + @Override + public void setUp() throws SQLException { + connection.createStatement().executeUpdate( + "CREATE TABLE IF NOT EXISTS `ENTRY` (" + + "`SHARED_ID` INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, " + + "`TYPE` VARCHAR(255) NOT NULL, " + + "`VERSION` INT(11) DEFAULT 1)"); + + connection.createStatement().executeUpdate( + "CREATE TABLE IF NOT EXISTS `FIELD` (" + + "`ENTRY_SHARED_ID` INT(11) NOT NULL, " + + "`NAME` VARCHAR(255) NOT NULL, " + + "`VALUE` TEXT DEFAULT NULL, " + + "FOREIGN KEY (`ENTRY_SHARED_ID`) REFERENCES `ENTRY`(`SHARED_ID`) ON DELETE CASCADE)"); + + connection.createStatement().executeUpdate("CREATE TABLE IF NOT EXISTS `METADATA` (" + + "`KEY` varchar(255) NOT NULL," + + "`VALUE` text NOT NULL)"); + } + + @Override + public String escape(String expression) { + return "`" + expression + "`"; + } +} diff --git a/src/main/java/net/sf/jabref/shared/OracleProcessor.java b/src/main/java/net/sf/jabref/shared/OracleProcessor.java new file mode 100644 index 000000000000..97d00fe014d6 --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/OracleProcessor.java @@ -0,0 +1,54 @@ +package net.sf.jabref.shared; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Processes all incoming or outgoing bib data to Oracle database and manages its structure. + */ +public class OracleProcessor extends DBMSProcessor { + + /** + * @param connection Working SQL connection + * @param dbmsType Instance of {@link DBMSType} + */ + public OracleProcessor(Connection connection) { + super(connection); + } + + /** + * Creates and sets up the needed tables and columns according to the database type. + * @throws SQLException + */ + @Override + public void setUp() throws SQLException { + connection.createStatement().executeUpdate( + "CREATE TABLE \"ENTRY\" (" + + "\"SHARED_ID\" NUMBER NOT NULL, " + + "\"TYPE\" VARCHAR2(255) NULL, " + + "\"VERSION\" NUMBER DEFAULT 1, " + + "CONSTRAINT \"ENTRY_PK\" PRIMARY KEY (\"SHARED_ID\"))"); + + connection.createStatement().executeUpdate("CREATE SEQUENCE \"ENTRY_SEQ\""); + + connection.createStatement().executeUpdate("CREATE TRIGGER \"ENTRY_T\" BEFORE INSERT ON \"ENTRY\" " + + "FOR EACH ROW BEGIN SELECT \"ENTRY_SEQ\".NEXTVAL INTO :NEW.shared_id FROM DUAL; END;"); + + connection.createStatement().executeUpdate( + "CREATE TABLE \"FIELD\" (" + + "\"ENTRY_SHARED_ID\" NUMBER NOT NULL, " + + "\"NAME\" VARCHAR2(255) NOT NULL, " + + "\"VALUE\" CLOB NULL, " + + "CONSTRAINT \"ENTRY_SHARED_ID_FK\" FOREIGN KEY (\"ENTRY_SHARED_ID\") " + + "REFERENCES \"ENTRY\"(\"SHARED_ID\") ON DELETE CASCADE)"); + + connection.createStatement().executeUpdate("CREATE TABLE \"METADATA\" (" + + "\"KEY\" VARCHAR2(255) NULL," + + "\"VALUE\" CLOB NOT NULL)"); + } + + @Override + public String escape(String expression) { + return "\"" + expression + "\""; + } +} diff --git a/src/main/java/net/sf/jabref/shared/PostgreSQLProcessor.java b/src/main/java/net/sf/jabref/shared/PostgreSQLProcessor.java new file mode 100644 index 000000000000..fe7840bbe91a --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/PostgreSQLProcessor.java @@ -0,0 +1,46 @@ +package net.sf.jabref.shared; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Processes all incoming or outgoing bib data to PostgreSQL database and manages its structure. + */ +public class PostgreSQLProcessor extends DBMSProcessor { + + /** + * @param connection Working SQL connection + * @param dbmsType Instance of {@link DBMSType} + */ + public PostgreSQLProcessor(Connection connection) { + super(connection); + } + + /** + * Creates and sets up the needed tables and columns according to the database type. + * @throws SQLException + */ + @Override + public void setUp() throws SQLException { + connection.createStatement().executeUpdate( + "CREATE TABLE IF NOT EXISTS \"ENTRY\" (" + + "\"SHARED_ID\" SERIAL PRIMARY KEY, " + + "\"TYPE\" VARCHAR, " + + "\"VERSION\" INTEGER DEFAULT 1)"); + + connection.createStatement().executeUpdate( + "CREATE TABLE IF NOT EXISTS \"FIELD\" (" + + "\"ENTRY_SHARED_ID\" INTEGER REFERENCES \"ENTRY\"(\"SHARED_ID\") ON DELETE CASCADE, " + + "\"NAME\" VARCHAR, " + + "\"VALUE\" TEXT)"); + + connection.createStatement().executeUpdate("CREATE TABLE IF NOT EXISTS \"METADATA\" (" + + "\"KEY\" VARCHAR," + + "\"VALUE\" TEXT)"); + } + + @Override + public String escape(String expression) { + return "\"" + expression + "\""; + } +} diff --git a/src/main/java/net/sf/jabref/shared/event/ConnectionLostEvent.java b/src/main/java/net/sf/jabref/shared/event/ConnectionLostEvent.java new file mode 100644 index 000000000000..798d19c46657 --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/event/ConnectionLostEvent.java @@ -0,0 +1,22 @@ +package net.sf.jabref.shared.event; + +import net.sf.jabref.BibDatabaseContext; + +/** + * A new {@link ConnectionLostEvent} is fired, when the connection to the shared database gets lost. + */ +public class ConnectionLostEvent { + + private final BibDatabaseContext bibDatabaseContext; + + /** + * @param bibDatabaseContext Affected {@link BibDatabaseContext} + */ + public ConnectionLostEvent(BibDatabaseContext bibDatabaseContext) { + this.bibDatabaseContext = bibDatabaseContext; + } + + public BibDatabaseContext getBibDatabaseContext() { + return this.bibDatabaseContext; + } +} diff --git a/src/main/java/net/sf/jabref/shared/event/SharedEntryNotPresentEvent.java b/src/main/java/net/sf/jabref/shared/event/SharedEntryNotPresentEvent.java new file mode 100644 index 000000000000..0a1a61298700 --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/event/SharedEntryNotPresentEvent.java @@ -0,0 +1,23 @@ +package net.sf.jabref.shared.event; + +import net.sf.jabref.model.entry.BibEntry; + +/** + * A new {@link SharedEntryNotPresentEvent} is fired, when the user tries to push changes of an obsolete + * {@link BibEntry} to the server. + */ +public class SharedEntryNotPresentEvent { + + private final BibEntry bibEntry; + + /** + * @param bibEntry Affected {@link BibEntry} + */ + public SharedEntryNotPresentEvent(BibEntry bibEntry) { + this.bibEntry = bibEntry; + } + + public BibEntry getBibEntry() { + return this.bibEntry; + } +} diff --git a/src/main/java/net/sf/jabref/shared/event/UpdateRefusedEvent.java b/src/main/java/net/sf/jabref/shared/event/UpdateRefusedEvent.java new file mode 100644 index 000000000000..1170f191e134 --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/event/UpdateRefusedEvent.java @@ -0,0 +1,37 @@ +package net.sf.jabref.shared.event; + +import net.sf.jabref.BibDatabaseContext; +import net.sf.jabref.model.entry.BibEntry; + +/** + * A new {@link UpdateRefusedEvent} is fired, when the user tries to push changes of an obsolete + * {@link BibEntry} to the server. + */ +public class UpdateRefusedEvent { + + private final BibDatabaseContext bibDatabaseContext; + private final BibEntry localBibEntry; + private final BibEntry sharedBibEntry; + + /** + * @param bibDatabaseContext Affected {@link BibDatabaseContext} + * @param bibEntry Affected {@link BibEntry} + */ + public UpdateRefusedEvent(BibDatabaseContext bibDatabaseContext, BibEntry localBibEntry, BibEntry sharedBibEntry) { + this.bibDatabaseContext = bibDatabaseContext; + this.localBibEntry = localBibEntry; + this.sharedBibEntry = sharedBibEntry; + } + + public BibDatabaseContext getBibDatabaseContext() { + return this.bibDatabaseContext; + } + + public BibEntry getLocalBibEntry() { + return localBibEntry; + } + + public BibEntry getSharedBibEntry() { + return sharedBibEntry; + } +} diff --git a/src/main/java/net/sf/jabref/shared/exception/OfflineLockException.java b/src/main/java/net/sf/jabref/shared/exception/OfflineLockException.java new file mode 100644 index 000000000000..e632dc3e2d94 --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/exception/OfflineLockException.java @@ -0,0 +1,28 @@ +package net.sf.jabref.shared.exception; + +import net.sf.jabref.model.entry.BibEntry; + +/** + * This exception is thrown if a BibEntry with smaller version number is going to be used for an update on the shared side. + * The principle of optimistic offline lock forbids updating with obsolete objects. + */ +public class OfflineLockException extends Exception { + + private final BibEntry localBibEntry; + private final BibEntry sharedBibEntry; + + + public OfflineLockException(BibEntry localBibEntry, BibEntry sharedBibEntry) { + super("Local BibEntry data is not up-to-date."); + this.localBibEntry = localBibEntry; + this.sharedBibEntry = sharedBibEntry; + } + + public BibEntry getLocalBibEntry() { + return localBibEntry; + } + + public BibEntry getSharedBibEntry() { + return sharedBibEntry; + } +} diff --git a/src/main/java/net/sf/jabref/shared/exception/SharedEntryNotPresentException.java b/src/main/java/net/sf/jabref/shared/exception/SharedEntryNotPresentException.java new file mode 100644 index 000000000000..332cae2c2c7f --- /dev/null +++ b/src/main/java/net/sf/jabref/shared/exception/SharedEntryNotPresentException.java @@ -0,0 +1,21 @@ +package net.sf.jabref.shared.exception; + +import net.sf.jabref.model.entry.BibEntry; + +/** + * This exception is thrown if a BibEntry is going to be updated while it does not exist on the shared side. + */ +public class SharedEntryNotPresentException extends Exception { + + private final BibEntry nonPresentBibEntry; + + + public SharedEntryNotPresentException(BibEntry nonPresentbibEntry) { + super("Required BibEntry is not present on shared database."); + this.nonPresentBibEntry = nonPresentbibEntry; + } + + public BibEntry getNonPresentBibEntry() { + return this.nonPresentBibEntry; + } +} diff --git a/src/main/java/net/sf/jabref/sql/DBConnectDialog.java b/src/main/java/net/sf/jabref/sql/DBConnectDialog.java deleted file mode 100644 index 7e657b759734..000000000000 --- a/src/main/java/net/sf/jabref/sql/DBConnectDialog.java +++ /dev/null @@ -1,291 +0,0 @@ -/* Copyright (C) 2003-2015 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -package net.sf.jabref.sql; - -import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import javax.swing.AbstractAction; -import javax.swing.ActionMap; -import javax.swing.BorderFactory; -import javax.swing.InputMap; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPasswordField; -import javax.swing.JTextField; -import javax.swing.SwingConstants; - -import net.sf.jabref.Globals; -import net.sf.jabref.gui.keyboard.KeyBinding; -import net.sf.jabref.logic.l10n.Localization; - -import com.jgoodies.forms.builder.ButtonBarBuilder; -import com.jgoodies.forms.builder.FormBuilder; -import com.jgoodies.forms.layout.FormLayout; - -/** - * Dialog box for collecting database connection strings from the user - * - * @author pattonlk - */ -public class DBConnectDialog extends JDialog { - - private static List FORMATTED_NAMES = Arrays - .stream(DatabaseType.values()) - .map(DatabaseType::getFormattedName) - .collect(Collectors.toList()); - - // input fields - private final JComboBox cmbServerType = new JComboBox<>(); - private final JTextField txtServerHostname = new JTextField(40); - private final JTextField txtDatabase = new JTextField(40); - private final JTextField txtUsername = new JTextField(40); - private final JPasswordField pwdPassword = new JPasswordField(40); - - private DBStrings dbStrings = new DBStrings(); - private boolean connectedToDB; - - /** - * Creates a new instance of DBConnectDialog - */ - public DBConnectDialog(JFrame parent, DBStrings dbs) { - super(Objects.requireNonNull(parent), Localization.lang("Connect to SQL database"), true); - - this.setResizable(false); - this.setLocationRelativeTo(parent); - - dbStrings = Objects.requireNonNull(dbs); - - // build collections of components - ArrayList lhs = new ArrayList<>(); - JLabel lblServerType = new JLabel(); - lhs.add(lblServerType); - JLabel lblServerHostname = new JLabel(); - lhs.add(lblServerHostname); - JLabel lblDatabase = new JLabel(); - lhs.add(lblDatabase); - JLabel lblUsername = new JLabel(); - lhs.add(lblUsername); - JLabel lblPassword = new JLabel(); - lhs.add(lblPassword); - - ArrayList rhs = new ArrayList<>(); - rhs.add(cmbServerType); - rhs.add(txtServerHostname); - rhs.add(txtDatabase); - rhs.add(txtUsername); - rhs.add(pwdPassword); - - // setup label text - lblServerType.setText(Localization.lang("Server type") + ':'); - lblServerHostname.setText(Localization.lang("Server hostname") + ':'); - lblDatabase.setText(Localization.lang("Database") + ':'); - lblUsername.setText(Localization.lang("Username") + ':'); - lblPassword.setText(Localization.lang("Password") + ':'); - - // set label text alignment - for (JLabel label : lhs) { - label.setHorizontalAlignment(SwingConstants.RIGHT); - } - - // set button text - JButton btnConnect = new JButton(); - btnConnect.setText(Localization.lang("Connect")); - JButton btnCancel = new JButton(); - btnCancel.setText(Localization.lang("Cancel")); - - // init input fields to current DB strings - for (String aSrv : FORMATTED_NAMES) { - cmbServerType.addItem(aSrv); - } - - cmbServerType.setSelectedItem(dbStrings.getDbPreferences().getServerType().getFormattedName()); - txtServerHostname.setText(dbStrings.getDbPreferences().getServerHostname()); - txtDatabase.setText(dbStrings.getDbPreferences().getDatabase()); - txtUsername.setText(dbStrings.getDbPreferences().getUsername()); - pwdPassword.setText(dbStrings.getPassword()); - - // construct dialog - FormBuilder builder = FormBuilder.create().layout(new - FormLayout("right:pref, 4dlu, fill:pref", "pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref")); - - builder.getPanel().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - - // add labels and input fields - builder.add(lblServerType).xy(1, 1); - builder.add(cmbServerType).xy(3, 1); - builder.add(lblServerHostname).xy(1, 3); - builder.add(txtServerHostname).xy(3, 3); - builder.add(lblDatabase).xy(1, 5); - builder.add(txtDatabase).xy(3, 5); - builder.add(lblUsername).xy(1, 7); - builder.add(txtUsername).xy(3, 7); - builder.add(lblPassword).xy(1, 9); - builder.add(pwdPassword).xy(3, 9); - - // add the panel to the CENTER of your dialog: - getContentPane().add(builder.getPanel(), BorderLayout.CENTER); - - // add buttons are added in a similar way: - ButtonBarBuilder bb = new ButtonBarBuilder(); - bb.addGlue(); - bb.addButton(btnConnect); - bb.addButton(btnCancel); - bb.addGlue(); - - // add the buttons to the SOUTH of your dialog: - getContentPane().add(bb.getPanel(), BorderLayout.SOUTH); - pack(); - - ActionListener connectAction = e -> { - Optional errorMessage = checkInput(); - - if (errorMessage.isPresent()) { - JOptionPane.showMessageDialog(null, errorMessage.get(), Localization.lang("Input error"), - JOptionPane.ERROR_MESSAGE); - } else { - storeSettings(); - setVisible(false); - setConnectToDB(true); - } - }; - - btnConnect.addActionListener(connectAction); - txtDatabase.addActionListener(connectAction); - txtServerHostname.addActionListener(connectAction); - txtUsername.addActionListener(connectAction); - pwdPassword.addActionListener(connectAction); - - AbstractAction cancelAction = new AbstractAction() { - - @Override - public void actionPerformed(ActionEvent e) { - setVisible(false); - dispose(); - setConnectToDB(false); - } - }; - btnCancel.addActionListener(cancelAction); - - // Key bindings: - ActionMap am = builder.getPanel().getActionMap(); - InputMap im = builder.getPanel().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); - am.put("close", cancelAction); - } - - /** - * Checks the user input, and ensures that required fields have entries - * - * @return Appropriate error message to be displayed to user - */ - private Optional checkInput() { - - String[] fields = {Localization.lang("Server hostname"), Localization.lang("Database"), - Localization.lang("Username")}; - String[] errors = new String[fields.length]; - int cnt = 0; - - if (txtServerHostname.getText().trim().isEmpty()) { - errors[cnt] = fields[0]; - cnt++; - } - - if (txtDatabase.getText().trim().isEmpty()) { - errors[cnt] = fields[1]; - cnt++; - } - - if (txtUsername.getText().trim().isEmpty()) { - errors[cnt] = fields[2]; - cnt++; - } - - StringBuilder errMsg = new StringBuilder(Localization.lang("Please specify the")).append(' '); - - switch (cnt) { - case 0: - errMsg = new StringBuilder(); - break; - case 1: - errMsg.append(errors[0]).append('.'); - break; - case 2: - errMsg.append(errors[0]).append(" and ").append(errors[1]).append('.'); - break; - default: // Will be 3 at most - errMsg.append(errors[0]).append(", ").append(errors[1]).append(", and ").append(errors[2]).append('.'); - break; - } - - if (errMsg.toString().isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(errMsg.toString()); - } - } - - /** - * Save user input. - */ - private void storeSettings() { - DBStringsPreferences preferences = new DBStringsPreferences( - cmbServerType.getSelectedItem().toString(), - txtServerHostname.getText(), - txtUsername.getText(), - txtDatabase.getText()); - - // Store these settings so they appear as default next time: - preferences.storeToPreferences(Globals.prefs); - - dbStrings.setDbPreferences(preferences); - - char[] pwd = pwdPassword.getPassword(); - String tmp = new String(pwd); - dbStrings.setPassword(tmp); - Arrays.fill(pwd, '0'); - - } - - public DBStrings getDBStrings() { - return dbStrings; - } - - public void setDBStrings(DBStrings dbStrings) { - this.dbStrings = dbStrings; - } - - public boolean isConnectedToDB() { - return connectedToDB; - } - - private void setConnectToDB(boolean connectToDB) { - this.connectedToDB = connectToDB; - } - -} diff --git a/src/main/java/net/sf/jabref/sql/DBExporterAndImporterFactory.java b/src/main/java/net/sf/jabref/sql/DBExporterAndImporterFactory.java deleted file mode 100644 index c6d69ff259d7..000000000000 --- a/src/main/java/net/sf/jabref/sql/DBExporterAndImporterFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright (C) 2003-2011 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -package net.sf.jabref.sql; - -import java.util.Objects; - -import net.sf.jabref.sql.database.MySQL; -import net.sf.jabref.sql.database.PostgreSQL; -import net.sf.jabref.sql.exporter.DatabaseExporter; -import net.sf.jabref.sql.importer.DatabaseImporter; - -public class DBExporterAndImporterFactory { - - /** - * ensuring that only one is used in the system - */ - private static final DatabaseExporter MYSQL_EXPORTER = new DatabaseExporter(new MySQL()); - - /** - * ensuring that only one is used in the system - */ - private static final DatabaseExporter POSTGRESQL_EXPORTER = new DatabaseExporter(new PostgreSQL()); - - public DatabaseExporter getExporter(DatabaseType type) { - Objects.requireNonNull(type); - - switch (type) { - case POSTGRESQL: - return POSTGRESQL_EXPORTER; - case MYSQL: - default: - return MYSQL_EXPORTER; - } - } - - public DatabaseImporter getImporter(DatabaseType type) { - Objects.requireNonNull(type); - - switch (type) { - case POSTGRESQL: - return new DatabaseImporter(new PostgreSQL()); - case MYSQL: - default: - return new DatabaseImporter(new MySQL()); - } - } -} diff --git a/src/main/java/net/sf/jabref/sql/DBImportExportDialog.java b/src/main/java/net/sf/jabref/sql/DBImportExportDialog.java deleted file mode 100644 index d4c7447a8fc3..000000000000 --- a/src/main/java/net/sf/jabref/sql/DBImportExportDialog.java +++ /dev/null @@ -1,261 +0,0 @@ -/* Copyright (C) 2003-2015 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -package net.sf.jabref.sql; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.ArrayList; -import java.util.List; -import java.util.Vector; - -import javax.swing.AbstractAction; -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.KeyStroke; -import javax.swing.ListSelectionModel; -import javax.swing.table.DefaultTableModel; - -import net.sf.jabref.gui.JabRefFrame; -import net.sf.jabref.logic.l10n.Localization; - -import com.jgoodies.forms.builder.ButtonBarBuilder; - -/** - * @author ifsteinm - */ -public class DBImportExportDialog implements MouseListener, KeyListener { - - private final JDialog diag; - private final JTable table; - - // IMPORT - public final List listOfDBs = new ArrayList<>(); - public boolean moreThanOne; - // EXPORT - public String selectedDB = ""; - public boolean hasDBSelected; - public boolean removeAction; - public int selectedInt = -1; - private final DialogType dialogType; - - public enum DialogType { - IMPORTER, EXPORTER - } - - - public DBImportExportDialog(JabRefFrame frame, Vector> rows, DialogType dialogType) { - this.dialogType = dialogType; - - Vector columns = new Vector<>(); - columns.add("Databases"); - table = new JTable(); - DefaultTableModel model = new DefaultTableModel(rows, columns) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - }; - - table.setModel(model); - - String dialogTitle; - String dialogTopMessage; - int tableSelectionModel; - if (isExporter()) { - dialogTitle = Localization.lang("SQL Database Exporter"); - dialogTopMessage = Localization.lang("Select target SQL database:"); - tableSelectionModel = ListSelectionModel.SINGLE_SELECTION; - table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put((KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)), "exportAction"); - table.getActionMap().put("exportAction", new AbstractAction() { - - @Override - public void actionPerformed(ActionEvent e) { - exportAction(); - } - }); - } else { - dialogTitle = Localization.lang("SQL Database Importer"); - dialogTopMessage = Localization.lang("Please select which JabRef databases do you want to import:"); - tableSelectionModel = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION; - table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put((KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)), "importAction"); - table.getActionMap().put("importAction", new AbstractAction() { - - @Override - public void actionPerformed(ActionEvent e) { - importAction(); - } - }); - } - - diag = new JDialog(frame, dialogTitle, false); - JPanel pan = new JPanel(); - pan.setLayout(new BorderLayout()); - - JLabel lab = new JLabel(dialogTopMessage); - lab.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - pan.add(lab, BorderLayout.NORTH); - - table.setSelectionMode(tableSelectionModel); - table.setPreferredScrollableViewportSize(new Dimension(100, 100)); - table.setTableHeader(null); - table.setRowSelectionInterval(0, 0); - - pan.add(new JScrollPane(table), BorderLayout.CENTER); - diag.getContentPane().add(pan, BorderLayout.NORTH); - pan = new JPanel(); - pan.setLayout(new BorderLayout()); - - diag.getContentPane().add(pan, BorderLayout.CENTER); - - ButtonBarBuilder b = new ButtonBarBuilder(); - b.addGlue(); - JButton importButton = new JButton(Localization.lang("Import")); - JButton exportButton = new JButton(Localization.lang("Export")); - if (isImporter()) { - b.addButton(importButton); - } else { - b.addButton(exportButton); - } - - b.addRelatedGap(); - JButton cancelButton = new JButton(Localization.lang("Cancel")); - b.addButton(cancelButton); - b.addRelatedGap(); - JButton removeButton = new JButton(Localization.lang("Remove selected")); - b.addButton(removeButton); - - b.addGlue(); - b.getPanel().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - diag.getContentPane().add(b.getPanel(), BorderLayout.SOUTH); - diag.pack(); - diag.setLocationRelativeTo(frame); - table.addMouseListener(this); - - importButton.addActionListener(e -> importAction()); - - exportButton.addActionListener(e -> exportAction()); - - cancelButton.addActionListener(e -> { - moreThanOne = false; - hasDBSelected = false; - diag.dispose(); - }); - - removeButton.addActionListener(e -> { - moreThanOne = false; - hasDBSelected = true; - selectedInt = table.getSelectedRow(); - selectedDB = (String) table.getValueAt(selectedInt, 0); - int areYouSure = JOptionPane.showConfirmDialog(diag, - Localization.lang("Are you sure you want to remove the already existent SQL DBs?")); - if (areYouSure == JOptionPane.YES_OPTION) { - removeAction = true; - diag.dispose(); - } - }); - diag.setModal(true); - diag.setVisible(true); - } - - private boolean isImporter() { - return this.dialogType == DialogType.IMPORTER; - } - - public boolean isExporter() { - return this.dialogType == DialogType.EXPORTER; - } - - public JDialog getDiag() { - return this.diag; - } - - private void exportAction() { - selectedInt = table.getSelectedRow(); - selectedDB = (String) table.getValueAt(selectedInt, 0); - hasDBSelected = true; - diag.dispose(); - } - - private void importAction() { - int[] selInt = table.getSelectedRows(); - for (int aSelectedInt : selInt) { - listOfDBs.add((String) table.getValueAt(aSelectedInt, 0)); - moreThanOne = true; - } - diag.dispose(); - } - - @Override - public void mouseClicked(MouseEvent e) { - if ((e.getClickCount() == 2) && isExporter()) { - this.exportAction(); - } - } - - @Override - public void mouseEntered(MouseEvent arg0) { - // TODO Auto-generated method stub - } - - @Override - public void mouseExited(MouseEvent arg0) { - // TODO Auto-generated method stub - - } - - @Override - public void mousePressed(MouseEvent arg0) { - // TODO Auto-generated method stub - - } - - @Override - public void mouseReleased(MouseEvent arg0) { - // TODO Auto-generated method stub - - } - - @Override - public void keyPressed(KeyEvent arg0) { - // TODO Auto-generated method stub - - } - - @Override - public void keyReleased(KeyEvent arg0) { - // TODO Auto-generated method stub - - } - - @Override - public void keyTyped(KeyEvent arg0) { - // TODO Auto-generated method stub - - } -} diff --git a/src/main/java/net/sf/jabref/sql/DBStrings.java b/src/main/java/net/sf/jabref/sql/DBStrings.java deleted file mode 100644 index 8f6c4f9d48a0..000000000000 --- a/src/main/java/net/sf/jabref/sql/DBStrings.java +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright (C) 2003-2016 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ -package net.sf.jabref.sql; - -import net.sf.jabref.Globals; - -/** - * @author pattonlk - */ -public class DBStrings { - - private DBStringsPreferences dbPreferences; - private String password; - private String dbParameters = ""; - private boolean isInitialized; - private boolean configValid; - - /** - * Creates a new instance of DBStrings - */ - public DBStrings() { - this.dbPreferences = new DBStringsPreferences(null, null, null, null); - this.setPassword(null); - this.isInitialized(false); - this.isConfigValid(false); - } - - /** - * Initializes the variables needed with defaults - */ - public void initialize() { - this.dbPreferences = DBStringsPreferences.loadFromPreferences(Globals.prefs); - setPassword(""); - isInitialized(true); - } - - public void setPassword(String password) { - this.password = password; - } - - public String getPassword() { - return password; - } - - public boolean isInitialized() { - return isInitialized; - } - - private void isInitialized(boolean isInit) { - this.isInitialized = isInit; - } - - public boolean isConfigValid() { - return configValid; - } - - public void isConfigValid(boolean confValid) { - this.configValid = confValid; - } - - /** - * Returns the database parameters set - * - * @return dbParameters: The concatenated parameters - */ - public String getDbParameters() { - return dbParameters; - } - - /** - * Add server specific database parameter(s)
- * Multiple parameters must be concatenated in the format
- * {@code ?Parameter1=value¶meter2=value2} - * - * @param dbParameter The concatendated parameter - */ - public void setDbParameters(String dbParameters) { - this.dbParameters = dbParameters; - } - - public DBStringsPreferences getDbPreferences() { - return dbPreferences; - } - - public void setDbPreferences(DBStringsPreferences dbPreferences) { - this.dbPreferences = dbPreferences; - } -} diff --git a/src/main/java/net/sf/jabref/sql/DBStringsPreferences.java b/src/main/java/net/sf/jabref/sql/DBStringsPreferences.java deleted file mode 100644 index 62d067b0add1..000000000000 --- a/src/main/java/net/sf/jabref/sql/DBStringsPreferences.java +++ /dev/null @@ -1,52 +0,0 @@ -package net.sf.jabref.sql; - -import net.sf.jabref.preferences.JabRefPreferences; - -public final class DBStringsPreferences { - - private final DatabaseType serverType; - private final String serverHostname; - private final String username; - private final String database; - - public DBStringsPreferences(String serverType, String serverHostname, String username, String database) { - this.serverType = DatabaseType.build(serverType).orElse(DatabaseType.MYSQL); - this.serverHostname = serverHostname; - this.username = username; - this.database = database; - } - - public DatabaseType getServerType() { - return serverType; - } - - public String getServerHostname() { - return serverHostname; - } - - public String getUsername() { - return username; - } - - public String getDatabase() { - return database; - } - - public static DBStringsPreferences loadFromPreferences(JabRefPreferences preferences) { - return new DBStringsPreferences( - preferences.get(JabRefPreferences.DB_CONNECT_SERVER_TYPE), - preferences.get(JabRefPreferences.DB_CONNECT_HOSTNAME), - preferences.get(JabRefPreferences.DB_CONNECT_USERNAME), - preferences.get(JabRefPreferences.DB_CONNECT_DATABASE)); - } - - /** - * Store these db strings into JabRef preferences. - */ - public void storeToPreferences(JabRefPreferences preferences) { - preferences.put(JabRefPreferences.DB_CONNECT_SERVER_TYPE, getServerType().name()); - preferences.put(JabRefPreferences.DB_CONNECT_HOSTNAME, getServerHostname()); - preferences.put(JabRefPreferences.DB_CONNECT_DATABASE, getDatabase()); - preferences.put(JabRefPreferences.DB_CONNECT_USERNAME, getUsername()); - } -} diff --git a/src/main/java/net/sf/jabref/sql/Database.java b/src/main/java/net/sf/jabref/sql/Database.java deleted file mode 100644 index f122b28da5bc..000000000000 --- a/src/main/java/net/sf/jabref/sql/Database.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.sf.jabref.sql; - -import java.sql.Connection; -import java.sql.SQLException; - -public interface Database { - - Connection connect(String url, String username, String password) - throws IllegalAccessException, InstantiationException, ClassNotFoundException, SQLException; - - String getReadColumnNamesQuery(); - - enum Table { - JABREF_DATABASE, ENTRY_TYPES, ENTRIES, STRINGS, GROUP_TYPES, GROUPS, ENTRY_GROUP - } - - String getCreateTableSQL(Table table); - - Connection connectAndEnsureDatabaseExists(DBStrings dbStrings) throws SQLException, IllegalAccessException, ClassNotFoundException, InstantiationException; - - DatabaseType getType(); - -} diff --git a/src/main/java/net/sf/jabref/sql/DatabaseType.java b/src/main/java/net/sf/jabref/sql/DatabaseType.java deleted file mode 100644 index 7efecd378c22..000000000000 --- a/src/main/java/net/sf/jabref/sql/DatabaseType.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.sf.jabref.sql; - -import java.util.Optional; - -/** - * All DBTypes must appear here. The enum items must be the - * names that appear in the combobox used to select the DB, - * because this text is used to choose which DatabaseImporter/Exporter - * will be sent back to the requester - */ -public enum DatabaseType { - MYSQL("MySQL"), POSTGRESQL("PostgreSQL"); - - private final String formattedName; - - DatabaseType(String formattedName) { - this.formattedName = formattedName; - } - - public String getFormattedName() { - return formattedName; - } - - public static Optional build(String serverType) { - for (DatabaseType type : values()) { - if (type.getFormattedName().equalsIgnoreCase(serverType)) { - return Optional.of(type); - } - } - return Optional.empty(); - } - - @Override - public String toString() { - return formattedName; - } - -} diff --git a/src/main/java/net/sf/jabref/sql/DatabaseUtil.java b/src/main/java/net/sf/jabref/sql/DatabaseUtil.java deleted file mode 100644 index 14b766553d63..000000000000 --- a/src/main/java/net/sf/jabref/sql/DatabaseUtil.java +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright (C) 2003-2016 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General public static License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General public static License for more details. - - You should have received a copy of the GNU General public static License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -package net.sf.jabref.sql; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import javax.swing.JOptionPane; - -import net.sf.jabref.BibDatabaseContext; - -public class DatabaseUtil { - - public static void removeDB(DBImportExportDialog dialogo, String dbName, Connection conn, BibDatabaseContext databaseContext) - throws SQLException { - if (dialogo.removeAction) { - if ((dialogo.selectedInt <= 0) && dialogo.isExporter()) { - JOptionPane.showMessageDialog(dialogo.getDiag(), "Please select a DB to be removed", "SQL Export", - JOptionPane.INFORMATION_MESSAGE); - } else { - removeDB(dbName, conn, databaseContext); - } - } - } - - public static void removeDB(String dbName, Connection conn, BibDatabaseContext databaseContext) - throws SQLException { - removeAGivenDB(conn, getDatabaseIDByName(databaseContext, conn, dbName)); - } - - /** - * Returns a Jabref Database ID from the database in case the DB is already exported. In case the BIB was already - * exported before, the method returns the id, otherwise it calls the method that inserts a new row and returns the - * ID for this new database - * - * @param databaseContext the database - * @param out The output (PrintStream or Connection) object to which the DML should be written. - * @return The ID of database row of the jabref database being exported - * @throws SQLException - */ - public static int getDatabaseIDByName(BibDatabaseContext databaseContext, Connection out, String dbName) - throws SQLException { - String query = "SELECT database_id FROM jabref_database WHERE database_name='" + dbName + "';"; - try (Statement statement = out.createStatement(); - ResultSet rs = statement.executeQuery(query)) { - if (rs.next()) { - return rs.getInt("database_id"); - } else { - insertJabRefDatabase(databaseContext, out, dbName); - return getDatabaseIDByName(databaseContext, out, dbName); - } - } - } - - private static void removeAGivenDB(Connection out, final int database_id) throws SQLException { - removeAllRecordsForAGivenDB(out, database_id); - SQLUtil.processQuery(out, "DELETE FROM jabref_database WHERE database_id='" + database_id + "';"); - } - - /** - * Removes all records for the database being exported in case it was exported before. - * - * @param out The output (PrintStream or Connection) object to which the DML should be written. - * @param database_id Id of the database being exported. - * @throws SQLException - */ - public static void removeAllRecordsForAGivenDB(Connection out, final int database_id) throws SQLException { - SQLUtil.processQuery(out, "DELETE FROM entries WHERE database_id='" + database_id + "';"); - SQLUtil.processQuery(out, "DELETE FROM groups WHERE database_id='" + database_id + "';"); - SQLUtil.processQuery(out, "DELETE FROM strings WHERE database_id='" + database_id + "';"); - } - - /** - * This method creates a new row into jabref_database table enabling to export more than one .bib - * - * @param databaseContext the database - * @param out The output (PrintStream or Connection) object to which the DML should be written. - * @throws SQLException - */ - private static void insertJabRefDatabase(final BibDatabaseContext databaseContext, Connection out, String dbName) - throws SQLException { - String path; - if (databaseContext.getDatabaseFile() == null) { - path = dbName; - } else { - path = databaseContext.getDatabaseFile().getAbsolutePath(); - } - SQLUtil.processQuery(out, - "INSERT INTO jabref_database(database_name, md5_path) VALUES ('" + dbName + "', md5('" + path + "'));"); - } - -} diff --git a/src/main/java/net/sf/jabref/sql/DbConnectAction.java b/src/main/java/net/sf/jabref/sql/DbConnectAction.java deleted file mode 100644 index 0417c9824819..000000000000 --- a/src/main/java/net/sf/jabref/sql/DbConnectAction.java +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright (C) 2003-2011 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -package net.sf.jabref.sql; - -import java.awt.event.ActionEvent; -import java.sql.Connection; - -import javax.swing.AbstractAction; -import javax.swing.JOptionPane; - -import net.sf.jabref.gui.BasePanel; -import net.sf.jabref.gui.actions.BaseAction; -import net.sf.jabref.logic.l10n.Localization; -import net.sf.jabref.sql.exporter.DatabaseExporter; - -/** - * Created by IntelliJ IDEA. User: alver Date: Mar 27, 2008 Time: 6:05:13 PM To - * change this template use File | Settings | File Templates. - *

- * Jan 20th Adjusted to accomodate changes on SQL Exporter module by ifsteinm - */ -public class DbConnectAction implements BaseAction { - - private final BasePanel panel; - - public DbConnectAction(BasePanel panel) { - this.panel = panel; - } - - public AbstractAction getAction() { - return new DbImpAction(); - } - - private class DbImpAction extends AbstractAction { - - @Override - public void actionPerformed(ActionEvent e) { - action(); - - } - } - - @Override - public void action() { - - DBStrings dbs = panel.getBibDatabaseContext().getMetaData().getDBStrings(); - - // init DB strings if necessary - if (!dbs.isInitialized()) { - dbs.initialize(); - } - - // show connection dialog - DBConnectDialog dbd = new DBConnectDialog(panel.frame(), dbs); - dbd.setLocationRelativeTo(panel); - dbd.setVisible(true); - - // connect to database to test DBStrings - if (!dbd.isConnectedToDB()) { - return; - } - - dbs = dbd.getDBStrings(); - - try { - - panel.frame().output( - Localization.lang("Establishing SQL connection...")); - DatabaseExporter exporter = new DBExporterAndImporterFactory().getExporter(dbs.getDbPreferences().getServerType()); - try (Connection conn = exporter.connectToDB(dbs)) { - // Nothing - } - dbs.isConfigValid(true); - panel.frame().output( - Localization.lang("SQL connection established.")); - } catch (Exception ex) { - String errorMessage = SQLUtil.getExceptionMessage(ex); - dbs.isConfigValid(false); - - String preamble = Localization.lang("Could not connect to SQL database for the following reason:"); - panel.frame().output(preamble + " " + errorMessage); - - JOptionPane.showMessageDialog(panel.frame(), preamble + '\n' + errorMessage, - Localization.lang("Connect to SQL database"), - JOptionPane.ERROR_MESSAGE); - } finally { - panel.getBibDatabaseContext().getMetaData().setDBStrings(dbs); - dbd.dispose(); - } - } -} diff --git a/src/main/java/net/sf/jabref/sql/SQLUtil.java b/src/main/java/net/sf/jabref/sql/SQLUtil.java deleted file mode 100644 index 78e86de4489e..000000000000 --- a/src/main/java/net/sf/jabref/sql/SQLUtil.java +++ /dev/null @@ -1,237 +0,0 @@ -/* Copyright (C) 2003-2015 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General public static License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General public static License for more details. - - You should have received a copy of the GNU General public static License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -package net.sf.jabref.sql; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import net.sf.jabref.model.entry.InternalBibtexFields; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * @author pattonlk - *

- * Reestructured by ifsteinm. Jan 20th Now it is possible to export more than one jabref database. BD creation, - * insertions and queries where reformulated to accomodate the changes. The changes include a refactory on - * import/export to SQL module, creating many other classes making them more readable This class just support - * Exporters and Importers - */ - -final public class SQLUtil { - - private static final List RESERVED_DB_WORDS = Collections.singletonList("key"); - - private static List allFields; - - private static final Log LOGGER = LogFactory.getLog(SQLUtil.class); - - private SQLUtil() { - } - - /** - * loop through entry types to get required, optional, general and utility fields for this type. - */ - private static void refreshFields() { - if (SQLUtil.allFields == null) { - SQLUtil.allFields = new ArrayList<>(); - } else { - SQLUtil.allFields.clear(); - } - SQLUtil.uniqueListInsert(SQLUtil.allFields, InternalBibtexFields.getAllFieldNames()); - SQLUtil.uniqueListInsert(SQLUtil.allFields, InternalBibtexFields.getAllPrivateFieldNames()); - } - - /** - * @return All existent fields for a bibtex entry - */ - public static List getAllFields() { - if (SQLUtil.allFields == null) { - SQLUtil.refreshFields(); - } - return SQLUtil.allFields; - } - - /** - * @return Create a common separated field names - */ - public static String getFieldStr() { - // create comma separated list of field names - List fieldNames = new ArrayList<>(); - for (int i = 0; i < SQLUtil.getAllFields().size(); i++) { - StringBuilder field = new StringBuilder(SQLUtil.allFields.get(i)); - if (SQLUtil.RESERVED_DB_WORDS.contains(field.toString())) { - field.append('_'); - } - fieldNames.add(field.toString()); - } - return String.join(", ", fieldNames); - } - - /** - * Inserts the elements of a List into another List making sure not to duplicate entries in the resulting List - * - * @param list1 The List containing unique entries - * @param list2 The second List to be inserted into the first List - * @return The updated list1 with new unique entries - */ - private static List uniqueListInsert(List list1, List list2) { - if (list2 != null) { - for (String fromList2 : list2) { - if (!list1.contains(fromList2) && (!"#".equals(fromList2))) { - list1.add(fromList2); - } - } - } - return list1; - } - - /** - * Generates DML specifying table columns and their datatypes. The output of this routine should be used within a - * CREATE TABLE statement. - * - * @param fields Contains unique field names - * @param datatype Specifies the SQL data type that the fields should take on. - * @return The SQL code to be included in a CREATE TABLE statement. - */ - public static String fieldsAsCols(List fields, String datatype) { - List newFields = new ArrayList<>(); - for (String field1 : fields) { - StringBuilder field = new StringBuilder(field1); - if (SQLUtil.RESERVED_DB_WORDS.contains(field.toString())) { - field.append('_'); - } - field.append(datatype); - newFields.add(field.toString()); - } - return String.join(", ", newFields); - } - - /** - * @param allFields All existent fields for a given entry type - * @param reqFields list containing required fields for an entry type - * @param optFields list containing optional fields for an entry type - * @param utiFields list containing utility fields for an entry type - * @param origList original list with the correct size filled with the default values for each field - * @return origList changing the values of the fields that appear on reqFields, optFields, utiFields set to 'req', - * 'opt' and 'uti' respectively - */ - public static List setFieldRequirement(List allFields, List reqFields, - List optFields, List utiFields, List origList) { - - String currentField; - for (int i = 0; i < allFields.size(); i++) { - currentField = allFields.get(i); - if (reqFields.contains(currentField)) { - origList.set(i, "req"); - } else if (optFields.contains(currentField)) { - origList.set(i, "opt"); - } else if (utiFields.contains(currentField)) { - origList.set(i, "uti"); - } - } - return origList; - } - - /** - * Return a message raised from a SQLException - * - * @param ex The SQLException raised - */ - public static String getExceptionMessage(Exception ex) { - String msg; - if (ex.getMessage() == null) { - msg = ex.toString(); - } else { - msg = ex.getMessage(); - } - return msg; - } - - /** - * return a Statement with the result of a "SELECT *" query for a given table - * - * @param conn Connection to the database - * @param tableName String containing the name of the table you want to get the results. - * @return a ResultSet with the query result returned from the DB - * @throws SQLException - */ - public static String queryAllFromTable(String tableName) throws SQLException { - return "SELECT * FROM " + tableName + ';'; - } - - /** - * Utility method for processing DML with proper output - * - * @param out The output Connection object to which the DML should be sent - * @param dml The DML statements to be processed - */ - public static void processQuery(Connection out, String dml) throws SQLException { - SQLUtil.executeQuery(out, dml); - } - - /** - * This routine returns the JDBC url corresponding to the DBStrings input. - * - * @param dbStrings The DBStrings to use to make the connection - * @return The JDBC url corresponding to the input DBStrings - */ - public static String createJDBCurl(DBStrings dbStrings, boolean withDBName) { - DBStringsPreferences preferences = dbStrings.getDbPreferences(); - return "jdbc:" + preferences.getServerType().getFormattedName().toLowerCase() + "://" + preferences.getServerHostname() - + (withDBName ? '/' + preferences.getDatabase() : "") + dbStrings.getDbParameters(); - } - - /** - * Process a query and returns only the first result of a result set as a String. To be used when it is certain that - * only one String (single cell) will be returned from the DB - * - * @param conn The Connection object to which the DML should be sent - * @param query The query statements to be processed - * @return String with the result returned from the database - * @throws SQLException - */ - public static String processQueryWithSingleResult(Connection conn, String query) throws SQLException { - try (Statement sm = conn.createStatement(); ResultSet rs = sm.executeQuery(query)) { - rs.next(); - return rs.getString(1); - } - } - - /** - * Utility method for executing DML - * - * @param conn The DML Connection object that will execute the SQL - * @param qry The DML statements to be executed - */ - private static void executeQuery(Connection conn, String qry) throws SQLException { - try (Statement stmnt = conn.createStatement()) { - stmnt.execute(qry); - SQLWarning warn = stmnt.getWarnings(); - if (warn != null) { - LOGGER.warn(warn); - } - } - } - -} diff --git a/src/main/java/net/sf/jabref/sql/database/MySQL.java b/src/main/java/net/sf/jabref/sql/database/MySQL.java deleted file mode 100644 index 0a6423a82a40..000000000000 --- a/src/main/java/net/sf/jabref/sql/database/MySQL.java +++ /dev/null @@ -1,137 +0,0 @@ -package net.sf.jabref.sql.database; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - -import net.sf.jabref.model.entry.IdGenerator; -import net.sf.jabref.sql.DBStrings; -import net.sf.jabref.sql.Database; -import net.sf.jabref.sql.DatabaseType; -import net.sf.jabref.sql.SQLUtil; - -public class MySQL implements Database { - - public static final String DRIVER = "com.mysql.jdbc.Driver"; - - - private void loadDriver() throws InstantiationException, IllegalAccessException, ClassNotFoundException { - Class.forName(DRIVER).newInstance(); - } - - @Override - public Connection connect(String url, String username, String password) - throws IllegalAccessException, InstantiationException, ClassNotFoundException, SQLException { - loadDriver(); - - return DriverManager.getConnection(url, username, password); - } - - @Override - public String getReadColumnNamesQuery() { - return "SHOW columns FROM entries;"; - } - - @Override - public String getCreateTableSQL(Table table) { - switch (table) { - - case JABREF_DATABASE: - return - "CREATE TABLE IF NOT EXISTS jabref_database ( \n" - + "database_id INT UNSIGNED NOT NULL AUTO_INCREMENT, \n" - + "database_name VARCHAR(64) NOT NULL, \n" - + "md5_path VARCHAR(32) NOT NULL, \n" - + "PRIMARY KEY (database_id)\n );"; - case ENTRY_TYPES: - return "CREATE TABLE IF NOT EXISTS entry_types ( \n" - + "entry_types_id INT UNSIGNED NOT NULL AUTO_INCREMENT, \n" - + "label TEXT, \n" - + SQLUtil.fieldsAsCols(SQLUtil.getAllFields(), - " VARCHAR(3) DEFAULT NULL") + ", \n" - + "PRIMARY KEY (entry_types_id) \n" + ");"; - case ENTRIES: - return - "CREATE TABLE IF NOT EXISTS entries ( \n" - + "entries_id INTEGER NOT NULL AUTO_INCREMENT, \n" - + "jabref_eid VARCHAR(" - + IdGenerator.getMinimumIntegerDigits() - + ") DEFAULT NULL, \n" - + "database_id INT UNSIGNED, \n" - + "entry_types_id INT UNSIGNED DEFAULT NULL, \n" - + "cite_key VARCHAR(100) DEFAULT NULL, \n" - + SQLUtil.fieldsAsCols(SQLUtil.getAllFields(), - " TEXT DEFAULT NULL") - + ",\n" - + "PRIMARY KEY (entries_id), \n" - + "INDEX(entry_types_id), \n" - + "FOREIGN KEY (entry_types_id) REFERENCES entry_types(entry_types_id), \n" - + "FOREIGN KEY (database_id) REFERENCES jabref_database(database_id) \n);"; - case STRINGS: - return "CREATE TABLE IF NOT EXISTS strings ( \n" - + "strings_id INTEGER NOT NULL AUTO_INCREMENT, \n" - + "label VARCHAR(100) DEFAULT NULL, \n" - + "content VARCHAR(200) DEFAULT NULL, \n" - + "database_id INT UNSIGNED, \n" - + "FOREIGN KEY (database_id) REFERENCES jabref_database(database_id), \n" - + "PRIMARY KEY (strings_id) \n" + ");"; - case GROUP_TYPES: - return "CREATE TABLE IF NOT EXISTS group_types ( \n" - + "group_types_id INTEGER NOT NULL AUTO_INCREMENT, \n" - + "label VARCHAR(100) DEFAULT NULL, \n" - + "PRIMARY KEY (group_types_id) \n" + ");"; - case GROUPS: - return - "CREATE TABLE IF NOT EXISTS groups ( \n" - + "groups_id INTEGER NOT NULL AUTO_INCREMENT, \n" - + "group_types_id INTEGER DEFAULT NULL, \n" - + "label VARCHAR(100) DEFAULT NULL, \n" - + "database_id INT UNSIGNED, \n" - + "parent_id INTEGER DEFAULT NULL, \n" - + "search_field VARCHAR(100) DEFAULT NULL, \n" - + "search_expression VARCHAR(200) DEFAULT NULL, \n" - + "case_sensitive BOOL DEFAULT NULL, \n" - + "reg_exp BOOL DEFAULT NULL, \n" - + "hierarchical_context INTEGER DEFAULT NULL, \n" - + "FOREIGN KEY (database_id) REFERENCES jabref_database(database_id), \n" - + "PRIMARY KEY (groups_id) \n" + ");"; - case ENTRY_GROUP: - return - "CREATE TABLE IF NOT EXISTS entry_group ( \n" - + "entries_id INTEGER NOT NULL AUTO_INCREMENT, \n" - + "groups_id INTEGER DEFAULT NULL, \n" - + "INDEX(entries_id), \n" - + "INDEX(groups_id), \n" - + "FOREIGN KEY (entries_id) REFERENCES entries(entries_id) ON DELETE CASCADE, \n" - + "FOREIGN KEY (groups_id) REFERENCES groups(groups_id), \n" - + "PRIMARY KEY (groups_id, entries_id) \n" + ");"; - default: - return ""; - } - - } - - private static final String OPT_ALLOW_MULTI_QUERIES = "?allowMultiQueries=true"; - - @Override - public Connection connectAndEnsureDatabaseExists(DBStrings dbStrings) - throws SQLException, IllegalAccessException, ClassNotFoundException, InstantiationException { - - dbStrings.setDbParameters(OPT_ALLOW_MULTI_QUERIES); - String url = SQLUtil.createJDBCurl(dbStrings, false); - - Connection conn = connect(url, dbStrings.getDbPreferences().getUsername(), dbStrings.getPassword()); - - String query = "CREATE DATABASE IF NOT EXISTS `" + dbStrings.getDbPreferences().getDatabase() + '`'; - SQLUtil.processQuery(conn, query); - conn.setCatalog(dbStrings.getDbPreferences().getDatabase()); - - return conn; - } - - @Override - public DatabaseType getType() { - return DatabaseType.MYSQL; - } -} - diff --git a/src/main/java/net/sf/jabref/sql/database/PostgreSQL.java b/src/main/java/net/sf/jabref/sql/database/PostgreSQL.java deleted file mode 100644 index e64f592cb81f..000000000000 --- a/src/main/java/net/sf/jabref/sql/database/PostgreSQL.java +++ /dev/null @@ -1,147 +0,0 @@ -package net.sf.jabref.sql.database; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - -import net.sf.jabref.model.entry.IdGenerator; -import net.sf.jabref.sql.DBStrings; -import net.sf.jabref.sql.Database; -import net.sf.jabref.sql.DatabaseType; -import net.sf.jabref.sql.SQLUtil; - -public class PostgreSQL implements Database { - - public static final String DRIVER = "org.postgresql.Driver"; - - private void loadDriver() throws ClassNotFoundException, IllegalAccessException, InstantiationException { - Class.forName(DRIVER).newInstance(); - } - - @Override - public Connection connect(String url, String username, String password) - throws IllegalAccessException, InstantiationException, ClassNotFoundException, SQLException { - loadDriver(); - - return DriverManager.getConnection(url, username, password); - } - - @Override - public String getReadColumnNamesQuery() { - return "SELECT column_name FROM information_schema.columns WHERE table_name ='entries';"; - } - - @Override - public String getCreateTableSQL(Table table) { - switch (table) { - - case JABREF_DATABASE: - return - "SELECT create_table_if_not_exists ('CREATE TABLE jabref_database ( \n" - + "database_id SERIAL NOT NULL, \n" - + "database_name VARCHAR(64) NOT NULL, \n" - + "md5_path VARCHAR(32) NOT NULL, \n" - + "PRIMARY KEY (database_id)\n );')"; - case ENTRY_TYPES: - return - "SELECT create_table_if_not_exists ('CREATE TABLE entry_types ( \n" - + "entry_types_id SERIAL, \n" - + "label TEXT, \n" - + SQLUtil.fieldsAsCols(SQLUtil.getAllFields(), - " VARCHAR(3) DEFAULT NULL") + ", \n" - + "PRIMARY KEY (entry_types_id) \n" + ");')"; - case ENTRIES: - return - "SELECT create_table_if_not_exists ('CREATE TABLE entries ( \n" - + "entries_id SERIAL, \n" - + "jabref_eid VARCHAR(" - + IdGenerator.getMinimumIntegerDigits() - + ") DEFAULT NULL, \n" - + "database_id INTEGER, \n" - + "entry_types_id INTEGER DEFAULT NULL, \n" - + "cite_key VARCHAR(100) DEFAULT NULL, \n" - + SQLUtil.fieldsAsCols(SQLUtil.getAllFields(), - " TEXT DEFAULT NULL") - + ",\n" - + "PRIMARY KEY (entries_id), \n" - + "FOREIGN KEY (entry_types_id) REFERENCES entry_types (entry_types_id), \n" - + "FOREIGN KEY (database_id) REFERENCES jabref_database(database_id) \n" - + ");')"; - case STRINGS: - return - "SELECT create_table_if_not_exists ('CREATE TABLE strings ( \n" - + "strings_id SERIAL, \n" - + "label VARCHAR(100) DEFAULT NULL, \n" - + "content VARCHAR(200) DEFAULT NULL, \n" - + "database_id INTEGER, \n" - + "FOREIGN KEY (database_id) REFERENCES jabref_database(database_id), \n" - + "PRIMARY KEY (strings_id) \n" + ");')"; - case GROUP_TYPES: - return - "SELECT create_table_if_not_exists ('CREATE TABLE group_types ( \n" - + "group_types_id SERIAL, \n" - + "label VARCHAR(100) DEFAULT NULL, \n" - + "PRIMARY KEY (group_types_id) \n" + ");')"; - case GROUPS: - return - "SELECT create_table_if_not_exists ('CREATE TABLE groups ( \n" - + "groups_id SERIAL, \n" - + "group_types_id INTEGER DEFAULT NULL, \n" - + "label VARCHAR(100) DEFAULT NULL, \n" - + "database_id INTEGER, \n" - + "parent_id INTEGER DEFAULT NULL, \n" - + "search_field VARCHAR(100) DEFAULT NULL, \n" - + "search_expression VARCHAR(200) DEFAULT NULL, \n" - + "case_sensitive BOOLEAN DEFAULT NULL, \n" - + "reg_exp BOOLEAN DEFAULT NULL, \n" - + "hierarchical_context INTEGER DEFAULT NULL, \n" - + "FOREIGN KEY (database_id) REFERENCES jabref_database(database_id), \n" - + "PRIMARY KEY (groups_id) \n" + ");')"; - case ENTRY_GROUP: - return - "SELECT create_table_if_not_exists ('CREATE TABLE entry_group ( \n" - + "entries_id SERIAL, \n" - + "groups_id INTEGER DEFAULT NULL, \n" - + "FOREIGN KEY (entries_id) REFERENCES entries (entries_id) ON DELETE CASCADE, \n" - + "FOREIGN KEY (groups_id) REFERENCES groups (groups_id), \n" - + "PRIMARY KEY (groups_id, entries_id) \n" + ");')"; - default: - return ""; - } - } - - @Override - public Connection connectAndEnsureDatabaseExists(DBStrings dbStrings) - throws SQLException, IllegalAccessException, ClassNotFoundException, InstantiationException { - - // requires that the database is already there - String url = SQLUtil.createJDBCurl(dbStrings, true); - - Connection conn = connect(url, - dbStrings.getDbPreferences().getUsername(), dbStrings.getPassword()); - - createPLPGSQLFunction(conn); - - return conn; - } - - @Override - public DatabaseType getType() { - return DatabaseType.POSTGRESQL; - } - - private void createPLPGSQLFunction(Connection conn) throws SQLException { - SQLUtil.processQuery( - conn, - "create or replace function create_table_if_not_exists (create_sql text) returns bool as $$" - + "BEGIN" - + "\tBEGIN" - + "\t\tEXECUTE create_sql;" - + "\t\tException when duplicate_table THEN" - + "\t\tRETURN false;" - + "\tEND;" - + "\tRETURN true;" - + "END;" + "$$" + "Language plpgsql;"); - } - -} diff --git a/src/main/java/net/sf/jabref/sql/exporter/DatabaseExporter.java b/src/main/java/net/sf/jabref/sql/exporter/DatabaseExporter.java deleted file mode 100644 index 644ccdf04236..000000000000 --- a/src/main/java/net/sf/jabref/sql/exporter/DatabaseExporter.java +++ /dev/null @@ -1,474 +0,0 @@ -/* Copyright (C) 2003-2016 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General public static License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General public static License for more details. - - You should have received a copy of the GNU General public static License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -package net.sf.jabref.sql.exporter; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Vector; -import java.util.stream.Collectors; - -import javax.swing.JOptionPane; - -import net.sf.jabref.BibDatabaseContext; -import net.sf.jabref.Globals; -import net.sf.jabref.gui.JabRefFrame; -import net.sf.jabref.logic.exporter.BibDatabaseWriter; -import net.sf.jabref.logic.exporter.SavePreferences; -import net.sf.jabref.logic.groups.AbstractGroup; -import net.sf.jabref.logic.groups.AllEntriesGroup; -import net.sf.jabref.logic.groups.ExplicitGroup; -import net.sf.jabref.logic.groups.GroupHierarchyType; -import net.sf.jabref.logic.groups.GroupTreeNode; -import net.sf.jabref.logic.groups.KeywordGroup; -import net.sf.jabref.logic.groups.SearchGroup; -import net.sf.jabref.logic.l10n.Localization; -import net.sf.jabref.logic.util.strings.StringUtil; -import net.sf.jabref.model.EntryTypes; -import net.sf.jabref.model.database.BibDatabase; -import net.sf.jabref.model.database.BibDatabaseMode; -import net.sf.jabref.model.entry.BibEntry; -import net.sf.jabref.model.entry.BibtexString; -import net.sf.jabref.model.entry.EntryType; -import net.sf.jabref.sql.DBImportExportDialog; -import net.sf.jabref.sql.DBStrings; -import net.sf.jabref.sql.Database; -import net.sf.jabref.sql.DatabaseUtil; -import net.sf.jabref.sql.SQLUtil; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * @author igorsteinmacher. - *

- * Jan 20th Abstract Class to provide main features to export entries to a DB. To insert a new DB it is - * necessary to extend this class and add the DB name the enum available at - * net.sf.jabref.sql.DBImporterAndExporterFactory (and to the GUI). This class and its subclasses create - * database, entries and related stuff within a DB. - */ - -public class DatabaseExporter { - - private static final Log LOGGER = LogFactory.getLog(DatabaseExporter.class); - - private final List dbNames = new ArrayList<>(); - private final Database database; - - private DBStrings dbStrings; - - public DatabaseExporter(Database database) { - this.database = database; - } - - /** - * Method for the exportDatabase methods. - * - * @param databaseContext the database to export - * @param entriesToExport The list of the entries to export. - * @param out The output (PrintStream or Connection) object to which the DML should be written. - * @throws SQLException - */ - public void performExport(BibDatabaseContext databaseContext, List entriesToExport, - Connection out, String dbName) throws SQLException { - - SavePreferences savePrefs = SavePreferences.loadForExportFromPreferences(Globals.prefs); - List entries = BibDatabaseWriter.getSortedEntries(databaseContext, entriesToExport, savePrefs); - GroupTreeNode gtn = databaseContext.getMetaData().getGroups(); - - final int databaseID = DatabaseUtil.getDatabaseIDByName(databaseContext, out, dbName); - DatabaseUtil.removeAllRecordsForAGivenDB(out, databaseID); - populateEntryTypesTable(out, databaseContext.getMode()); - populateEntriesTable(databaseID, entries, out); - populateStringTable(databaseContext.getDatabase(), out, databaseID); - populateGroupTypesTable(out); - populateGroupsTable(gtn, 0, 1, out, databaseID); - populateEntryGroupsTable(gtn, 0, 1, out, databaseID); - } - - /** - * Generates the DML required to populate the entries table with jabref data and writes it to the output - * PrintStream. - * - * @param database_id ID of Jabref database related to the entries to be exported This information can be gathered - * using getDatabaseIDByPath(metaData, connection) - * @param entries The BibtexEntries to export - * @param connection The output (PrintStream or Connection) object to which the DML should be written. - */ - private void populateEntriesTable(final int database_id, List entries, Connection connection) - throws SQLException { - for (BibEntry entry : entries) { - try (PreparedStatement statement = connection.prepareStatement( - "INSERT INTO entries (jabref_eid, entry_types_id, cite_key, " + SQLUtil.getFieldStr() + ", database_id) " - + "VALUES (?, (SELECT entry_types_id FROM entry_types WHERE label= ? ), ?, " + SQLUtil.getAllFields().stream().map(s -> "?").collect(Collectors.joining(", ")) + ", ?);")) { - statement.setString(1, entry.getId()); - statement.setString(2, entry.getType()); - statement.setString(3, entry.getCiteKey()); - int value = 4; - for (String field : SQLUtil.getAllFields()) { - statement.setString(value, entry.getField(field)); - value++; - } - statement.setInt(value, database_id); - - statement.execute(); - } - } - } - - /** - * Recursive method to include a tree of groups. - * - * @param cursor The current GroupTreeNode in the GroupsTree - * @param parentID The integer ID associated with the cursors's parent node - * @param currentID The integer value to associate with the cursor - * @param connection The Connection - * @param database_id Id of jabref database to which the group is part of - */ - - private int populateEntryGroupsTable(GroupTreeNode cursor, int parentID, int currentID, Connection connection, - final int database_id) { - - if (cursor == null) { - // no groups passed - return -1; - } - - // recurse on child nodes (depth-first traversal) - String sql = "SELECT groups_id FROM groups WHERE label = ? AND database_id= ? AND parent_id = ? ;"; - try (PreparedStatement statement = connection.prepareStatement(sql)) { - - statement.setString(1, cursor.getGroup().getName()); - statement.setInt(2, database_id); - statement.setInt(3, parentID); - try (ResultSet resultSet = statement.executeQuery()) { - - // setting values to ID and myID to be used in case of textual SQL - // export - currentID++; - int myID = currentID; - resultSet.next(); - myID = resultSet.getInt("groups_id"); - - for (GroupTreeNode child : cursor.getChildren()) { - currentID = populateEntryGroupsTable(child, myID, currentID, connection, database_id); - } - - } - //Unfortunatley, AutoCloseable throws only Exception - } catch (SQLException e) { - LOGGER.warn("Cannot close resource", e); - } - return currentID; - } - - /** - * Generates the SQL required to populate the entry_types table with jabref data. - * - * @param out The output (PrintSream or Connection) object to which the DML should be written. - * @param type - */ - - private void populateEntryTypesTable(Connection out, BibDatabaseMode type) throws SQLException { - List fieldRequirement = new ArrayList<>(); - - List existentTypes = new ArrayList<>(); - try (Statement sm = out.createStatement(); - ResultSet rs = sm.executeQuery("SELECT label FROM entry_types")) { - while (rs.next()) { - existentTypes.add(rs.getString(1)); - } - } - for (EntryType val : EntryTypes.getAllValues(type)) { - StringBuilder querySB = new StringBuilder(); - - fieldRequirement.clear(); - for (int i = 0; i < SQLUtil.getAllFields().size(); i++) { - fieldRequirement.add(i, "gen"); - } - List reqFields = val.getRequiredFieldsFlat(); - List optFields = val.getOptionalFields(); - List utiFields = Collections.singletonList("search"); - fieldRequirement = SQLUtil.setFieldRequirement(SQLUtil.getAllFields(), reqFields, optFields, utiFields, - fieldRequirement); - if (existentTypes.contains(val.getName().toLowerCase())) { - String[] update = SQLUtil.getFieldStr().split(","); - querySB.append("UPDATE entry_types SET \n"); - for (int i = 0; i < fieldRequirement.size(); i++) { - querySB.append(update[i]).append("='").append(fieldRequirement.get(i)).append("',"); - } - querySB.delete(querySB.lastIndexOf(","), querySB.length()); - querySB.append(" WHERE label='").append(val.getName().toLowerCase()).append("';"); - } else { - querySB.append("INSERT INTO entry_types (label, ").append(SQLUtil.getFieldStr()).append(") VALUES ('") - .append(val.getName().toLowerCase()).append('\''); - for (String aFieldRequirement : fieldRequirement) { - querySB.append(", '").append(aFieldRequirement).append('\''); - } - querySB.append(");"); - } - SQLUtil.processQuery(out, querySB.toString()); - } - } - - /** - * Recursive worker method for the populateGroupsTable methods. - * - * @param cursor The current GroupTreeNode in the GroupsTree - * @param parentID The integer ID associated with the cursors's parent node - * @param currentID The integer value to associate with the cursor - * @param out The output (PrintStream or Connection) object to which the DML should be written. - * @param database_id Id of jabref database to which the groups/entries are part of - */ - private int populateGroupsTable(GroupTreeNode cursor, int parentID, int currentID, Connection out, - final int database_id) throws SQLException { - - if (cursor == null) { - // no groups passed - return -1; - } - - AbstractGroup group = cursor.getGroup(); - String searchField = null; - String searchExpr = null; - String caseSens = null; - String regExp = null; - GroupHierarchyType hierContext = group.getHierarchicalContext(); - if (group instanceof KeywordGroup) { - searchField = ((KeywordGroup) group).getSearchField(); - searchExpr = ((KeywordGroup) group).getSearchExpression(); - caseSens = ((KeywordGroup) group).isCaseSensitive() ? "1" : "0"; - regExp = ((KeywordGroup) group).isRegExp() ? "1" : "0"; - } else if (group instanceof SearchGroup) { - searchExpr = ((SearchGroup) group).getSearchExpression(); - caseSens = ((SearchGroup) group).isCaseSensitive() ? "1" : "0"; - regExp = ((SearchGroup) group).isRegExp() ? "1" : "0"; - } - // Protect all quotes in the group descriptions: - if (searchField != null) { - searchField = StringUtil.quote(searchField, "'", '\\'); - } - if (searchExpr != null) { - searchExpr = StringUtil.quote(searchExpr, "'", '\\'); - } - - SQLUtil.processQuery(out, "INSERT INTO groups (label, parent_id, group_types_id, search_field, " - + "search_expression, case_sensitive, reg_exp, hierarchical_context, database_id) " + "VALUES ('" - + group.getName() + "', " + parentID + ", (SELECT group_types_id FROM group_types where label='" - + group.getTypeId() + "')" + ", " + (searchField != null ? '\'' + searchField + '\'' : "NULL") + ", " - + (searchExpr != null ? '\'' + searchExpr + '\'' : "NULL") + ", " - + (caseSens != null ? '\'' + caseSens + '\'' : "NULL") + ", " - + (regExp != null ? '\'' + regExp + '\'' : "NULL") + ", " + hierContext.ordinal() + ", '" + database_id - + "');"); - - // recurse on child nodes (depth-first traversal) - try (Statement statement = out.createStatement(); - ResultSet rs = statement.executeQuery( - "SELECT groups_id FROM groups WHERE label='" + cursor.getGroup().getName() + "' AND database_id='" - + database_id + "' AND parent_id='" + parentID + "';")) { - // setting values to ID and myID to be used in case of textual SQL - // export - int myID = currentID; - rs.next(); - myID = rs.getInt("groups_id"); - for (GroupTreeNode child : cursor.getChildren()) { - currentID++; - currentID = populateGroupsTable(child, myID, currentID, out, database_id); - } - } catch (SQLException e) { - LOGGER.warn("Cannot close resource", e); - } - - return currentID; - } - - /** - * Generates the DML required to populate the group_types table with JabRef data. - * - * @param out The output (PrintSream or Connection) object to which the DML should be written. - * @throws SQLException - */ - private static void populateGroupTypesTable(Connection out) throws SQLException { - int quantity = 0; - - try (Statement sm = out.createStatement(); - ResultSet res = sm.executeQuery("SELECT COUNT(*) AS amount FROM group_types")) { - res.next(); - quantity = res.getInt("amount"); - } - - if (quantity == 0) { - String[] typeNames = new String[] {AllEntriesGroup.ID, ExplicitGroup.ID, KeywordGroup.ID, SearchGroup.ID}; - for (String typeName : typeNames) { - String insert = "INSERT INTO group_types (label) VALUES ('" + typeName + "');"; - SQLUtil.processQuery(out, insert); - } - } - } - - /** - * Generates the SQL required to populate the strings table with jabref data. - * - * @param database BibDatabase object used from where the strings will be exported - * @param out The output (PrintStream or Connection) object to which the DML should be written. - * @param database_id ID of Jabref database related to the entries to be exported This information can be gathered - * using getDatabaseIDByPath(metaData, out) - * @throws SQLException - */ - private static void populateStringTable(BibDatabase database, Connection out, final int database_id) - throws SQLException { - String insert = "INSERT INTO strings (label, content, database_id) VALUES ("; - - if (database.getPreamble() != null) { - String dml = insert + "'@PREAMBLE', " + '\'' + StringUtil.quote(database.getPreamble(), "'", '\\') + "', " - + '\'' + database_id + "');"; - SQLUtil.processQuery(out, dml); - } - for (String key : database.getStringKeySet()) { - BibtexString string = database.getString(key); - String dml = insert + '\'' + StringUtil.quote(string.getName(), "'", '\\') + "', " + '\'' - + StringUtil.quote(string.getContent(), "'", '\\') + "', " + '\'' + database_id + '\'' + ");"; - SQLUtil.processQuery(out, dml); - } - } - - /** - * Given a DBStrings it connects to the DB and returns the java.sql.Connection object - * - * @param dbstrings The DBStrings to use to make the connection - * @return java.sql.Connection to the DB chosen - * @throws Exception - */ - public Connection connectToDB(DBStrings dbstrings) throws Exception { - this.dbStrings = dbstrings; - - return database.connectAndEnsureDatabaseExists(dbStrings); - } - - /** - * Generates DML code necessary to create all tables in a database, and writes it to appropriate output. - * - * @param out The output (PrintStream or Connection) object to which the DML should be written. - */ - public void createTables(Connection out) throws SQLException { - for (Database.Table table : Database.Table.values()) { - SQLUtil.processQuery(out, database.getCreateTableSQL(table)); - } - } - - /** - * Accepts the BibDatabase and MetaData, generates the DML required to create and populate SQL database tables, - * and writes this DML to the specified SQL database. - * - * @param databaseContext the database to export - * @param entriesToExport The list of the entries to export. - * @param databaseStrings The necessary database connection information - */ - public void exportDatabaseToDBMS(final BibDatabaseContext databaseContext, - List entriesToExport, DBStrings databaseStrings, JabRefFrame frame) throws Exception { - String dbName; - boolean redisplay = false; - try(Connection conn = this.connectToDB(databaseStrings)) { - try { - createTables(conn); - Vector> matrix = createExistentDBNamesMatrix(databaseStrings); - DBImportExportDialog dialogo = new DBImportExportDialog(frame, matrix, - DBImportExportDialog.DialogType.EXPORTER); - if (dialogo.removeAction) { - dbName = getDBName(matrix, databaseStrings, frame, dialogo); - DatabaseUtil.removeDB(dialogo, dbName, conn, databaseContext); - redisplay = true; - } else if (dialogo.hasDBSelected) { - dbName = getDBName(matrix, databaseStrings, frame, dialogo); - performExport(databaseContext, entriesToExport, conn, dbName); - } - if (!conn.getAutoCommit()) { - conn.commit(); - conn.setAutoCommit(true); - } - if (redisplay) { - exportDatabaseToDBMS(databaseContext, entriesToExport, databaseStrings, frame); - } - } catch (SQLException ex) { - if ((conn != null) && !conn.getAutoCommit()) { - conn.rollback(); - } - throw ex; - } - } - } - - private String getDBName(Vector> matrix, DBStrings databaseStrings, JabRefFrame frame, - DBImportExportDialog dialogo) throws Exception { - String dbName = ""; - if (matrix.size() > 1) { - if (dialogo.hasDBSelected) { - dbName = dialogo.selectedDB; - if ((dialogo.selectedInt == 0) && (!dialogo.removeAction)) { - dbName = JOptionPane.showInputDialog(dialogo.getDiag(), - Localization.lang("Please enter the desired name:"), Localization.lang("SQL Export"), - JOptionPane.INFORMATION_MESSAGE); - if (dbName == null) { - getDBName(matrix, databaseStrings, frame, - new DBImportExportDialog(frame, matrix, DBImportExportDialog.DialogType.EXPORTER)); - } else { - while (!isValidDBName(dbNames, dbName)) { - dbName = JOptionPane.showInputDialog(dialogo.getDiag(), - Localization.lang("You have entered an invalid or already existent DB name.") + '\n' - + Localization.lang("Please enter the desired name:"), - Localization.lang("SQL Export"), JOptionPane.ERROR_MESSAGE); - } - } - } - } - } else { - dbName = JOptionPane.showInputDialog(frame, Localization.lang("Please enter the desired name:"), - Localization.lang("SQL Export"), JOptionPane.INFORMATION_MESSAGE); - } - return dbName; - } - - private Vector> createExistentDBNamesMatrix(DBStrings databaseStrings) throws Exception { - try (Connection conn = this.connectToDB(databaseStrings); - Statement statement = conn.createStatement(); - ResultSet rs = statement.executeQuery(SQLUtil.queryAllFromTable("jabref_database"))) { - - Vector v; - Vector> matrix = new Vector<>(); - dbNames.clear(); - v = new Vector<>(); - v.add(Localization.lang("< CREATE NEW DATABASE >")); - matrix.add(v); - while (rs.next()) { - v = new Vector<>(); - v.add(rs.getString("database_name")); - matrix.add(v); - dbNames.add(rs.getString("database_name")); - } - return matrix; - } - } - - private boolean isValidDBName(List databaseNames, String desiredName) { - return (desiredName != null) && (desiredName.trim().length() > 1) && !databaseNames.contains(desiredName); - } - -} diff --git a/src/main/java/net/sf/jabref/sql/importer/DBImporterResult.java b/src/main/java/net/sf/jabref/sql/importer/DBImporterResult.java deleted file mode 100644 index 3d1c02498659..000000000000 --- a/src/main/java/net/sf/jabref/sql/importer/DBImporterResult.java +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (C) 2003-2015 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General public static License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General public static License for more details. - - You should have received a copy of the GNU General public static License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -package net.sf.jabref.sql.importer; - -import net.sf.jabref.BibDatabaseContext; -import net.sf.jabref.MetaData; -import net.sf.jabref.model.database.BibDatabase; - -public class DBImporterResult { - - private final BibDatabaseContext databaseContext; - private final String name; - - public DBImporterResult(final BibDatabase database, final MetaData metaData, final String name) { - this.databaseContext = new BibDatabaseContext(database, metaData); - this.name = name; - } - - public String getName() { - return name; - } - - public BibDatabaseContext getDatabaseContext() { - return databaseContext; - } - -} diff --git a/src/main/java/net/sf/jabref/sql/importer/DatabaseImporter.java b/src/main/java/net/sf/jabref/sql/importer/DatabaseImporter.java deleted file mode 100644 index c1fa312dfdd6..000000000000 --- a/src/main/java/net/sf/jabref/sql/importer/DatabaseImporter.java +++ /dev/null @@ -1,308 +0,0 @@ -/* Copyright (C) 2003-2015 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General public static License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General public static License for more details. - - You should have received a copy of the GNU General public static License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -package net.sf.jabref.sql.importer; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.StringJoiner; -import java.util.stream.Collectors; - -import net.sf.jabref.Globals; -import net.sf.jabref.MetaData; -import net.sf.jabref.importer.fileformat.ParseException; -import net.sf.jabref.logic.groups.AbstractGroup; -import net.sf.jabref.logic.groups.AllEntriesGroup; -import net.sf.jabref.logic.groups.ExplicitGroup; -import net.sf.jabref.logic.groups.GroupHierarchyType; -import net.sf.jabref.logic.groups.GroupTreeNode; -import net.sf.jabref.logic.groups.KeywordGroup; -import net.sf.jabref.logic.groups.SearchGroup; -import net.sf.jabref.logic.util.strings.StringUtil; -import net.sf.jabref.model.EntryTypes; -import net.sf.jabref.model.database.BibDatabase; -import net.sf.jabref.model.database.BibDatabaseMode; -import net.sf.jabref.model.entry.BibEntry; -import net.sf.jabref.model.entry.BibtexString; -import net.sf.jabref.model.entry.EntryType; -import net.sf.jabref.model.entry.IdGenerator; -import net.sf.jabref.sql.DBStrings; -import net.sf.jabref.sql.Database; -import net.sf.jabref.sql.SQLUtil; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * @author ifsteinm. - *

- * Jan 20th Abstract Class to provide main features to import entries from a DB. To insert a new DB it is - * necessary to extend this class and add the DB name the enum available at - * net.sf.jabref.sql.DBImporterAndExporterFactory (and to the GUI). This class and its subclasses import - * database, entries and related stuff from a DB to bib. Each exported database is imported as a new JabRef - * (bib) database, presented on a new tab - */ -public class DatabaseImporter { - - private static final Log LOGGER = LogFactory.getLog(DatabaseImporter.class); - - private static final List COLUMNS_NOT_CONSIDERED_FOR_ENTRIES = Arrays.asList( - "cite_key", - "entry_types_id", - "database_id", - "jabref_eid", - "entries_id" - ); - - - private final Database database; - - public DatabaseImporter(Database database) { - this.database = database; - } - - /** - * @param conn Connection object to the database - * @return A ResultSet with column name for the entries table - * @throws SQLException - */ - private List readColumnNames(Connection conn) throws SQLException { - String query = database.getReadColumnNamesQuery(); - try (Statement statement = conn.createStatement(); - ResultSet rsColumns = statement.executeQuery(query)) { - List colNames = new ArrayList<>(); - while (rsColumns.next()) { - colNames.add(rsColumns.getString(1)); - } - return colNames; - } - } - - /** - * Worker method to perform the import from a database - * - * @param dbs The necessary database connection information - * @param mode - * @return An ArrayList containing pairs of Objects. Each position of the ArrayList stores three Objects: a - * BibDatabase, a MetaData and a String with the BIB database name stored in the DBMS - * @throws SQLException - * @throws ClassNotFoundException - * @throws InstantiationException - * @throws IllegalAccessException - * @throws Exception - */ - public List performImport(DBStrings dbs, List listOfDBs, BibDatabaseMode mode) - throws IllegalAccessException, InstantiationException, ClassNotFoundException, SQLException - { - List result = new ArrayList<>(); - try (Connection conn = this.connectToDB(dbs)) { - - Iterator itLista = listOfDBs.iterator(); - StringJoiner stringJoiner = new StringJoiner(",", "(", ")"); - - while (itLista.hasNext()) { - stringJoiner.add("'" + itLista.next() + "'"); - } - - String query = SQLUtil.queryAllFromTable( - "jabref_database WHERE database_name IN " + stringJoiner.toString()); - try (Statement statement = conn.createStatement(); - ResultSet rsDatabase = statement.executeQuery(query)) { - while (rsDatabase.next()) { - BibDatabase database = new BibDatabase(); - // Find entry type IDs and their mappings to type names: - HashMap types = new HashMap<>(); - try (Statement entryTypes = conn.createStatement(); - ResultSet rsEntryType = entryTypes.executeQuery(SQLUtil.queryAllFromTable("entry_types"))) { - while (rsEntryType.next()) { - Optional entryType = EntryTypes.getType(rsEntryType.getString("label"), mode); - if (entryType.isPresent()) { - types.put(rsEntryType.getString("entry_types_id"), entryType.get()); - } - } - } - - List colNames = this.readColumnNames(conn).stream().filter(column -> !COLUMNS_NOT_CONSIDERED_FOR_ENTRIES.contains(column)).collect(Collectors.toList()); - - final String database_id = rsDatabase.getString("database_id"); - // Read the entries and create BibEntry instances: - HashMap entries = new HashMap<>(); - try (Statement entryStatement = conn.createStatement(); - ResultSet rsEntries = entryStatement.executeQuery(SQLUtil.queryAllFromTable( - "entries WHERE database_id= '" + database_id + "';"))) { - while (rsEntries.next()) { - String id = rsEntries.getString("entries_id"); - BibEntry entry = new BibEntry(IdGenerator.next(), types.get(rsEntries.getString("entry_types_id")).getName()); - entry.setCiteKey(rsEntries.getString("cite_key")); - for (String col : colNames) { - String value = rsEntries.getString(col); - if (value != null) { - col = col.charAt(col.length() - 1) == '_' ? col.substring(0, - col.length() - 1) : col; - entry.setField(col, value); - } - } - entries.put(id, entry); - database.insertEntry(entry); - } - } - // Import strings and preamble: - try (Statement stringStatement = conn.createStatement(); - ResultSet rsStrings = stringStatement.executeQuery(SQLUtil.queryAllFromTable( - "strings WHERE database_id='" + database_id + '\''))) { - while (rsStrings.next()) { - String label = rsStrings.getString("label"); - String content = rsStrings.getString("content"); - if ("@PREAMBLE".equals(label)) { - database.setPreamble(content); - } else { - BibtexString string = new BibtexString(IdGenerator.next(), label, content); - database.addString(string); - } - } - } - MetaData metaData = new MetaData(); - metaData.initializeNewDatabase(); - // Read the groups tree: - importGroupsTree(metaData, entries, conn, database_id); - result.add(new DBImporterResult(database, metaData, rsDatabase.getString("database_name"))); - } - } - } - - return result; - } - - /** - * Look up the group type name from the type ID in the database. - * - * @param groupId The database's groups id - * @param conn The database connection - * @return The name (JabRef type id) of the group type. - * @throws SQLException - */ - private String findGroupTypeName(String groupId, Connection conn) throws SQLException { - return SQLUtil.processQueryWithSingleResult(conn, - "SELECT label FROM group_types WHERE group_types_id='" + groupId + "';"); - } - - private void importGroupsTree(MetaData metaData, Map entries, Connection conn, - final String database_id) throws SQLException { - Map groups = new HashMap<>(); - LinkedHashMap parentIds = new LinkedHashMap<>(); - GroupTreeNode rootNode = GroupTreeNode.fromGroup(new AllEntriesGroup()); - - String query = SQLUtil.queryAllFromTable("groups WHERE database_id='" + database_id + "' ORDER BY groups_id"); - try (Statement statement = conn.createStatement(); - ResultSet rsGroups = statement.executeQuery(query)) { - while (rsGroups.next()) { - AbstractGroup group = null; - String typeId = findGroupTypeName(rsGroups.getString("group_types_id"), conn); - try { - switch (typeId) { - case AllEntriesGroup.ID: - // register the id of the root node: - groups.put(rsGroups.getString("groups_id"), rootNode); - break; - case ExplicitGroup.ID: - group = new ExplicitGroup(rsGroups.getString("label"), - GroupHierarchyType.getByNumber(rsGroups.getInt("hierarchical_context")), Globals.prefs); - break; - case KeywordGroup.ID: - LOGGER.debug("Keyw: " + rsGroups.getBoolean("case_sensitive")); - group = new KeywordGroup(rsGroups.getString("label"), - StringUtil.unquote(rsGroups.getString("search_field"), '\\'), - StringUtil.unquote(rsGroups.getString("search_expression"), '\\'), - rsGroups.getBoolean("case_sensitive"), rsGroups.getBoolean("reg_exp"), - GroupHierarchyType.getByNumber(rsGroups.getInt("hierarchical_context")), Globals.prefs); - break; - case SearchGroup.ID: - LOGGER.debug("Search: " + rsGroups.getBoolean("case_sensitive")); - group = new SearchGroup(rsGroups.getString("label"), - StringUtil.unquote(rsGroups.getString("search_expression"), '\\'), - rsGroups.getBoolean("case_sensitive"), rsGroups.getBoolean("reg_exp"), - GroupHierarchyType.getByNumber(rsGroups.getInt("hierarchical_context"))); - break; - default: - break; - } - } catch (ParseException e) { - LOGGER.error(e); - } - - if (group != null) { - GroupTreeNode node = GroupTreeNode.fromGroup(group); - parentIds.put(node, rsGroups.getString("parent_id")); - groups.put(rsGroups.getString("groups_id"), node); - } - - // Ok, we have collected a map of all groups and their parent IDs, - // and another map of all group IDs and their group nodes. - // Now we need to build the groups tree: - for (Map.Entry groupTreeNodeStringEntry : parentIds.entrySet()) { - String parentId = groupTreeNodeStringEntry.getValue(); - GroupTreeNode parent = groups.get(parentId); - if (parent == null) { - // TODO: missing parent - } else { - groupTreeNodeStringEntry.getKey().moveTo(parent); - } - } - - try (Statement entryGroup = conn.createStatement(); - ResultSet rsEntryGroup = entryGroup.executeQuery(SQLUtil.queryAllFromTable("entry_group"))) { - while (rsEntryGroup.next()) { - String entryId = rsEntryGroup.getString("entries_id"); - String groupId = rsEntryGroup.getString("groups_id"); - GroupTreeNode node = groups.get(groupId); - if ((node != null) && (node.getGroup() instanceof ExplicitGroup)) { - ExplicitGroup expGroup = (ExplicitGroup) node.getGroup(); - expGroup.add(entries.get(entryId)); - } - } - } - metaData.setGroups(rootNode); - } - } - } - - /** - * Given a DBStrings it connects to the DB and returns the java.sql.Connection object - * - * @param dbstrings The DBStrings to use to make the connection - * @return java.sql.Connection to the DB chosen - * @throws SQLException - * @throws ClassNotFoundException - * @throws InstantiationException - * @throws IllegalAccessException - */ - public Connection connectToDB(DBStrings dbstrings) - throws IllegalAccessException, InstantiationException, ClassNotFoundException, SQLException { - String url = SQLUtil.createJDBCurl(dbstrings, true); - return database.connect(url, dbstrings.getDbPreferences().getUsername(), dbstrings.getPassword()); - } - -} diff --git a/src/main/java/net/sf/jabref/sql/importer/DbImportAction.java b/src/main/java/net/sf/jabref/sql/importer/DbImportAction.java deleted file mode 100644 index cbef631868ca..000000000000 --- a/src/main/java/net/sf/jabref/sql/importer/DbImportAction.java +++ /dev/null @@ -1,220 +0,0 @@ -/* Copyright (C) 2003-2015 JabRef contributors. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -package net.sf.jabref.sql.importer; - -import java.awt.event.ActionEvent; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.Statement; -import java.util.List; -import java.util.Vector; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.JOptionPane; - -import net.sf.jabref.BibDatabaseContext; -import net.sf.jabref.Globals; -import net.sf.jabref.gui.BasePanel; -import net.sf.jabref.gui.JabRefFrame; -import net.sf.jabref.gui.actions.MnemonicAwareAction; -import net.sf.jabref.gui.worker.AbstractWorker; -import net.sf.jabref.gui.worker.CallBack; -import net.sf.jabref.gui.worker.Worker; -import net.sf.jabref.logic.l10n.Localization; -import net.sf.jabref.sql.DBConnectDialog; -import net.sf.jabref.sql.DBExporterAndImporterFactory; -import net.sf.jabref.sql.DBImportExportDialog; -import net.sf.jabref.sql.DBStrings; -import net.sf.jabref.sql.DatabaseUtil; -import net.sf.jabref.sql.SQLUtil; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Created by IntelliJ IDEA. User: alver Date: Mar 27, 2008 Time: 6:09:08 PM To change this template use File | Settings - * | File Templates. - *

- * Jan. 20th Changed to accommodate the new way to connect to DB and also to show the exceptions and to display more than - * one DB imported (by ifsteinm) - */ -public class DbImportAction extends AbstractWorker { - - private static final Log LOGGER = LogFactory.getLog(DbImportAction.class); - - private BibDatabaseContext databaseContext; - private boolean connectedToDB; - private final JabRefFrame frame; - private DBStrings dbs; - private List databases; - - public DbImportAction(JabRefFrame frame) { - this.frame = frame; - } - - public AbstractAction getAction() { - return new DbImpAction(); - } - - class DbImpAction extends MnemonicAwareAction { - public DbImpAction() { - putValue(Action.NAME, Localization.menuTitle("Import from external SQL database")); - } - - @Override - public void actionPerformed(ActionEvent e) { - try { - runInSeparateThread(); - } catch (Throwable throwable) { - LOGGER.warn("Problem importing from database", throwable); - } - } - } - - @Override - public void init() { - - dbs = new DBStrings(); - dbs.initialize(); - DBConnectDialog dbd = new DBConnectDialog(frame, dbs); - dbs = dbd.getDBStrings(); - // panel.metaData().getDBStrings(); - - // get DBStrings from user if necessary - if (dbs.isConfigValid()) { - connectedToDB = true; - } else { - // init DB strings if necessary - if (!dbs.isInitialized()) { - dbs.initialize(); - } - - // show connection dialog - dbd = new DBConnectDialog(frame, dbs); - dbd.setLocationRelativeTo(frame); - dbd.setVisible(true); - - connectedToDB = dbd.isConnectedToDB(); - - // store database strings - if (connectedToDB) { - dbs = dbd.getDBStrings(); - dbd.dispose(); - } - } - - } - - @Override - public void run() { - performImport(); - } - - private void performImport() { - if (!connectedToDB) { - return; - } - - frame.output(Localization.lang("Attempting SQL import...")); - DBExporterAndImporterFactory factory = new DBExporterAndImporterFactory(); - DatabaseImporter importer = factory.getImporter(dbs.getDbPreferences().getServerType()); - try { - try (Connection conn = importer.connectToDB(dbs); - Statement statement = conn.createStatement(); - ResultSet rs = statement.executeQuery(SQLUtil.queryAllFromTable("jabref_database"))) { - - Vector> matrix = new Vector<>(); - - while (rs.next()) { - Vector v = new Vector<>(); - v.add(rs.getString("database_name")); - matrix.add(v); - } - - if (matrix.isEmpty()) { - JOptionPane.showMessageDialog(frame, - Localization.lang("There are no available databases to be imported"), - Localization.lang("Import from SQL database"), JOptionPane.INFORMATION_MESSAGE); - } else { - DBImportExportDialog dialogo = new DBImportExportDialog(frame, matrix, DBImportExportDialog.DialogType.IMPORTER); - if (dialogo.removeAction) { - String dbName = dialogo.selectedDB; - DatabaseUtil.removeDB(dialogo, dbName, conn, databaseContext); - performImport(); - } else if (dialogo.moreThanOne) { - // use default DB mode for import - databases = importer.performImport(dbs, dialogo.listOfDBs, Globals.prefs.getDefaultBibDatabaseMode()); - for (DBImporterResult res : databases) { - databaseContext = res.getDatabaseContext(); - dbs.isConfigValid(true); - } - frame.output(Localization.lang("%0 databases will be imported", - Integer.toString(databases.size()))); - } else { - frame.output(Localization.lang("Importing canceled")); - } - } - } - } catch (Exception ex) { - String preamble = Localization.lang("Could not import from SQL database for the following reason:"); - String errorMessage = SQLUtil.getExceptionMessage(ex); - dbs.isConfigValid(false); - JOptionPane.showMessageDialog(frame, preamble + '\n' + errorMessage, - Localization.lang("Import from SQL database"), JOptionPane.ERROR_MESSAGE); - frame.output(Localization.lang("Error importing from database")); - LOGGER.error("Error importing from database", ex); - } - } - - @Override - public void update() { - if (databases == null) { - return; - } - for (DBImporterResult res : databases) { - databaseContext = res.getDatabaseContext(); - if (databaseContext != null) { - BasePanel pan = frame.addTab(databaseContext, true); - pan.getBibDatabaseContext().getMetaData().setDBStrings(dbs); - frame.setTabTitle(pan, res.getName() + "(Imported)", "Imported DB"); - pan.markBaseChanged(); - } - } - frame.output(Localization.lang("Imported %0 databases successfully", Integer.toString(databases.size()))); - } - - private void runInSeparateThread() throws Throwable { - // This part uses Spin's features: - Worker wrk = this.getWorker(); - // The Worker returned by getWorker() has been wrapped - // by Spin.off(), which makes its methods be run in - // a different thread from the EDT. - CallBack clb = this.getCallBack(); - - this.init(); // This method runs in this same thread, the EDT. - // Useful for initial GUI actions, like printing a message. - - // The CallBack returned by getCallBack() has been wrapped - // by Spin.over(), which makes its methods be run on - // the EDT. - wrk.run(); // Runs the potentially time-consuming action - // without freezing the GUI. The magic is that THIS line - // of execution will not continue until run() is finished. - clb.update(); // Runs the update() method on the EDT. - } - -} diff --git a/src/main/resources/l10n/JabRef_da.properties b/src/main/resources/l10n/JabRef_da.properties index 5d40435913e3..6244833f3c8d 100644 --- a/src/main/resources/l10n/JabRef_da.properties +++ b/src/main/resources/l10n/JabRef_da.properties @@ -65,7 +65,6 @@ Assigned_%0_entries_to_group_"%1".=Tilføjede_%0_poster_til_gruppen_"%1". Assigned_1_entry_to_group_"%0".=Tilføjede_1_post_til_gruppen_"%0". Attach_URL=Tilføj_URL Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Forsøg_at_sætte_fil-link_automatisk_for_dine_poster._Dette_virker,_hvis_en_fil_i_dit_fil-bibliotek_eller_et_underbibliotek
har_navn_lignende_en_posts_BibTeX-nøgle,_plus_efternavn. -Attempting_SQL_export...=Forsøger_SQL-eksport... Auto=Auto Autodetect_format=Autodetekter_format Autogenerate_BibTeX_keys=Autogenerer_BibTeX-nøgler @@ -132,7 +131,6 @@ Color_for_marking_incomplete_entries=Farve_til_markering_af_ufuldstændige_poste Column_width=Kolonnebredde Command_line_id=Kommandolinje-id Connect=Tilslut -Connect_to_SQL_database=Tilslut_til_SQL-database Contained_in=Indeholdt_i Content=Indhold Copied=Kopierede @@ -174,9 +172,10 @@ Customize_key_bindings=Opsætning_af_genvejstaster Cut=Klip cut_entries=klippede_poster cut_entry=klip_post -Database=Database Database_encoding=Tegnkoding_for_database Database_properties=Databaseegenskaber + +Database_type= Date_format=Datoformat Default=Standard Default_encoding=Standard_kodning @@ -271,7 +270,6 @@ Error_while_writing=En_fejl_opstod_ved_skrivning Error_writing_to_%0_file(s).=Fejl_ved_skrivning_til_%0_fil(er). -Establishing_SQL_connection...=Etablerer_SQL-forbindelse... Exceptions=Fejlinformation Existing_file=Eksisterende_fil '%0'_exists._Overwrite_file?='%0'_eksisterer._Erstat_filen? @@ -283,7 +281,6 @@ Export_preferences=Eksporter_indstillinger Export_preferences_to_file=Eksporter_indstillinger_til_fil Export_properties=Egenskaber_for_eksportfilter Export_to_clipboard=Eksporter_til_udklipsholder -Export_to_SQL_database=Eksporter_til_SQL-database Exporting=Eksporterer Extension=Efternavn External_changes=Eksterne_ændringer @@ -522,6 +519,7 @@ Open_database=Åbn_database Open_editor_when_a_new_entry_is_created=Start_redigering_når_en_ny_post_oprettes Open_file=Åbn_fil Open_last_edited_databases_at_startup=Åbn_sidst_viste_databaser_ved_opstart +Open_shared_database= Open_right-click_menu_with_Ctrl+left_button=Åbn_højreklikmenu_med_Ctrl+venstre_knap Open_terminal_here= Open_URL_or_DOI=Åbn_URL_eller_DOI @@ -564,7 +562,6 @@ Please_enter_the_field_to_search_(e.g._keywords)_and_the_keyword_to_searc Please_enter_the_string's_label=Skriv_et_navn_for_strengen Please_select_an_importer.=Vælg_venligst_et_importfilter. Please_select_exactly_one_group_to_move.=Vælg_præcis_én_gruppe_til_flytning. -Please_specify_the=Angiv_venligst Possible_duplicate_entries=Mulige_dubletter Possible_duplicate_of_existing_entry._Click_to_resolve.=Mulig_dublet_af_eksisterende_post._Klik_for_at_løse_problemet. Preamble=Præambel @@ -577,6 +574,7 @@ Problem_with_parsing_entry=Problem_med_at_læse_post Processing_%0=Arbejder_%0 Program_output=Output_fra_program +Pull_changes_from_shared_database= Pushed_citations_to_%0=Referencer_sendt_til_%0 Quit_JabRef=Afslut_JabRef Quit_synchronization=Afslut_synkronisering @@ -664,8 +662,6 @@ Select_file_from_ZIP-archive=Vælg_fil_fra_ZIP-fil Select_new_ImportFormat_subclass=Vælg_klasse_til_nyt_importformat Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Vælg_forgreningerne_for_at_inspicere_og_acceptere_eller_forkaste_ændringer Selected_entries=Valgte_poster -Server_hostname=Server_værtsnavn -Server_type=Server-type Set_field=Sæt_felt Set_fields=Sæt_felter @@ -707,7 +703,6 @@ Sorted_immediate_subgroups.=Sorterede_nærmeste_undergrupper. source_edit=redigering_af_kilde Special_name_formatters=Specielle_navneformateringer Special_table_columns=Specielle_kolonner -SQL_connection_established.=SQL-forbindelse_oprettet. Starting_import=Starter_import Statically_group_entries_by_manual_assignment=Grupper_poster_statisk_ved_manuel_tildeling Status=Status @@ -845,8 +840,6 @@ Fetching_Medline_by_term...=Henter_fra_Medline_via_udtryk... %0_import_canceled=%0-import_afbrudt Please_enter_a_valid_number=Indtast_venligst_et_gyldigt_tal Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=Indtast_venligst_en_kommasepareret_liste_af_Medline_IDer_(numre)_eller_søgeudtryk. -Connect_to_external_SQL_database=Tilslut_til_ekstern_SQL-database -Export_to_external_SQL_database=Eksporter_til_ekstern_SQL-database Show_search_results_in_a_window=Vis_søgeresultater_i_et_vindue Move_file_to_file_directory?=Flyt_fil_til_filbibliotek? @@ -861,7 +854,6 @@ Database_protection=Database-beskyttelse Unable_to_save_database=Kan_ikke_gemme_database BibTeX_key_generator=BibTeX-nøglegenerator Unable_to_open_link.=Kan_ikke_åbne_link. -Attempting_SQL_import...=Forsøger_SQL-import... Move_the_keyboard_focus_to_the_entry_table=Flyt_tastatur-fokus_til_hovedtabellen MIME_type=MIME-type @@ -1101,21 +1093,10 @@ Doing_a_cleanup_for_%0_entries...= No_entry_needed_a_clean_up= One_entry_needed_a_clean_up= %0_entries_needed_a_clean_up= -Error_importing_from_database=Fejl_ved_import_fra_database Error_downloading_file_'%0'=Feil_ved_hentning_af_filen_'%0' Download_failed=Hentning_mislykkedes -%0_databases_will_be_imported=%0_databaser_vil_blive_importeret -Importing_canceled=Import_afbrudt -There_are_no_available_databases_to_be_imported=Det_er_ingen_databaser_tilgængelige_for_import -Import_from_SQL_database=Import_fra_SQL-database -Imported_%0_databases_successfully=Importerede_%0_databaser -<_CREATE_NEW_DATABASE_>=<_OPRET_NY_DATABASE_> Remove_selected=Fjern_valgte -SQL_Database_Exporter=Eksporter_SQL-database -Select_target_SQL_database\:=Vælg_destinationsdatabase\: -SQL_Database_Importer=Importer_SQL-database -Please_select_which_JabRef_databases_do_you_want_to_import\:=Vælg_venligst_hvilke_JabRef-databaser,_du_vil_importere\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.= Attach_file= @@ -1255,7 +1236,6 @@ Hostname= Invalid_setting= Network= Please_specify_both_hostname_and_port= -Port= Use_custom_proxy_configuration= Clear_connection_settings= Cleared_connection_settings.= @@ -1348,9 +1328,6 @@ Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modif Added_entry= Added_new_'%0'_entry.= Choose_the_URL_to_download.= -Could_not_connect_to_SQL_database_for_the_following_reason\:= -Could_not_export_to_SQL_database_for_the_following_reason\:= -Could_not_import_from_SQL_database_for_the_following_reason\:= Deleted_entry= Discard_changes= Donate_to_JabRef= @@ -1496,7 +1473,6 @@ wrong_entry_type_as_proceedings_has_page_numbers= Abbreviate_journal_names= Abbreviating...= Adding_fetched_entries= -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?= Display_keywords_appearing_in_ALL_entries= Display_keywords_appearing_in_ANY_entry= Fetching_entries_from_Inspire= @@ -1512,11 +1488,8 @@ Ill-formed_entrytype_comment_in_BIB_file= Clipboard= Could_not_paste_entry_as_text\:= Do_you_still_want_to_continue?= -Please_enter_the_desired_name\:= -SQL_Export= This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:= This_could_cause_undesired_changes_to_your_entries.= -You_have_entered_an_invalid_or_already_existent_DB_name.= Disable_highlight_groups_matching_entries= Run_field_formatter\:= @@ -1741,3 +1714,36 @@ Entry_from_%0=Post_fra_%0 Merge_entry_with_%0_information= Updated_entry_with_info_from_%0= +Connection= +Host= +Port= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_de.properties b/src/main/resources/l10n/JabRef_de.properties index bdf63306d909..411602c91804 100644 --- a/src/main/resources/l10n/JabRef_de.properties +++ b/src/main/resources/l10n/JabRef_de.properties @@ -121,7 +121,6 @@ Assigned_1_entry_to_group_"%0".=1_Eintrag_zu_Gruppe_"%0"_hinzugefügt. Attach_URL=URL_anfügen Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Versucht,_Datei-Links_für_die_Einträge_automatisch_zuzuordnen._Dies_funktioniert,_wenn_der_Name_einer_Datei_im_Datei-Verzeichnis_oder_einem_Unterverzeichnis
identisch_ist_mit_dem_BibTeX-Key_eines_Eintrags_(erweitert_um_die_jeweilige_Dateiendung). -Attempting_SQL_export...=Versuche_SQL-Export... Auto=Auto @@ -259,9 +258,6 @@ Column_width=Spaltenbreite Command_line_id=Kommandozeilen_ID -Connect=Verbinden -Connect_to_SQL_database=Mit_SQL-Datenbank_verbinden - Contained_in=Enthalten_in Content=Inhalt @@ -326,12 +322,13 @@ cut_entries=Einträge_ausschneiden cut_entry=Eintrag_ausschneiden -Database=Datenbank Database_encoding=Zeichenkodierung_der_Datei Database_properties=Eigenschaften_der_Datei +Database_type=Typ der Datenbank + Date_format=Datumsformat Default=Standard @@ -503,7 +500,6 @@ Error_while_writing=Fehler_beim_Schreiben Error_writing_to_%0_file(s).=Fehler_beim_Schreiben_in_%0_Datei(en). -Establishing_SQL_connection...=SQL-Verbindung_wird_aufgebaut... Exceptions=Ausnahmen @@ -525,7 +521,6 @@ Export_preferences_to_file=Exportiere_Einstellungen_in_Datei Export_properties=Eigenschaften_für_Exportfilter Export_to_clipboard=In_die_Zwischenablage_kopieren -Export_to_SQL_database=Export_in_SQL-Datenbank Exporting=Exportiere Extension=Erweiterung @@ -963,6 +958,8 @@ Open_file=Datei_öffnen Open_last_edited_databases_at_startup=Beim_Starten_von_JabRef_die_letzten_bearbeiteten_Dateien_öffnen +Open_shared_database=Öffne entfernte Datenbank + Open_right-click_menu_with_Ctrl+left_button=Kontextmenü_mit_Strg_+_linker_Maustaste_öffnen Open_terminal_here=Konsole_hier_öffnen @@ -1034,7 +1031,6 @@ Please_enter_the_string's_label=Geben_Sie_bitte_den_Namen_des_Strings_ein. Please_select_an_importer.=Bitte_Importer_auswählen. Please_select_exactly_one_group_to_move.=Bitte_genau_eine_zu_bewegende_Gruppe_auswählen. -Please_specify_the=Bitte_geben_Sie_folgendes_an\: Possible_duplicate_entries=Mögliche_doppelte_Einträge @@ -1058,6 +1054,7 @@ Processing_%0=Bearbeite_%0 Program_output=Programmausgabe +Pull_changes_from_shared_database=Datenbank-Änderungen_beziehen Pushed_citations_to_%0=Einträge_in_%0_eingefügt @@ -1238,8 +1235,6 @@ Select_new_ImportFormat_subclass=Klasse_auswählen Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Wählen_Sie_die_Verzweigungen_aus,_um_die_Änderungen_zu_sehen_und_anzunehmen_oder_zu_verwerfen Selected_entries=Ausgewählte_Einträge -Server_hostname=Hostname_des_Servers -Server_type=Servertyp Set_field=Setze_Feld Set_fields=Felder_setzen @@ -1316,7 +1311,6 @@ source_edit=Quelltextbearbeitung Special_name_formatters=Spezielle_Namens-Formatierer Special_table_columns=Spezielle_Spalten -SQL_connection_established.=SQL-Verbindung_hergestellt. @@ -1551,8 +1545,6 @@ Fetching_Medline_by_term...=Rufe_Medline_mittels_Suchbegriff_ab... %0_import_canceled=%0-Import_abgebrochen Please_enter_a_valid_number=Bitte_geben_Sie_eine_gültige_Zahl_ein Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=Bitte_geben_Sie_eine_durch_Kommas_unterteilte_Liste_von_Medline-IDs_(Zahlen)_oder_Suchausdrücken_ein. -Connect_to_external_SQL_database=Mit_externer_SQL-Datenbank_verbinden -Export_to_external_SQL_database=Export_in_externe_SQL-Datenbank Show_search_results_in_a_window=Suchergebnisse_in_einem_Fenster_anzeigen Move_file_to_file_directory?=Datei_in_Dateiverzeichnis_verschieben? @@ -1568,7 +1560,6 @@ Database_protection=Dateischutz Unable_to_save_database=Speichern_der_Datei_nicht_möglich BibTeX_key_generator=BibTeX-Key-Generator Unable_to_open_link.=Öffnen_des_Links_nicht_möglich -Attempting_SQL_import...=Versuche,_SQL_zu_importieren... Move_the_keyboard_focus_to_the_entry_table=Tastatur-Fokus_auf_die_Tabelle_setzen MIME_type=MIME-Typ @@ -1806,22 +1797,11 @@ Doing_a_cleanup_for_%0_entries...=Aufräumen_von_%0_Einträgen... No_entry_needed_a_clean_up=Aufräumen_bei_keinem_Eintrag_nötig One_entry_needed_a_clean_up=Aufräumen_bei_einem_Eintrag_nötig %0_entries_needed_a_clean_up=Aufräumen_bei_%0_Einträgen_nötig -Error_importing_from_database=Fehler_beim_Import_von_der_Datei Error_downloading_file_'%0'=Fehler_beim_Herunterladen_der_Datei_'%0' Download_failed=Download_fehlgeschlagen -%0_databases_will_be_imported=%0_Dateien_werden_importiert -Importing_canceled=Import_abgebrochen -There_are_no_available_databases_to_be_imported=Es_stehen_keine_Dateien_zum_Import_zur_Verfügung -Import_from_SQL_database=Import_von_einer_SQL-Datenbank -Imported_%0_databases_successfully=%0_Dateien_wurden_erfolgreich_importiert -<_CREATE_NEW_DATABASE_>=<_NEUE_DATENBANK_ERSTELLEN_> Remove_selected=Ausgewählte_löschen -SQL_Database_Exporter=SQL-Datenbank-Exporter -Select_target_SQL_database\:=Wählen_Sie_die_SQL-Zieldatenbank\: -SQL_Database_Importer=SQL-Datenbank-Importer -Please_select_which_JabRef_databases_do_you_want_to_import\:=Bitte_wählen_Sie,_welche_JabRef-Dateien_Sie_importieren_wollen\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.=Der_Gruppenbaum_konnte_nicht_geparsed_werden._Wenn_Sie_die_BibTeX-Datei_speichern,_gehen_alle_Gruppen_verloren. Attach_file=Datei_anhängen @@ -1961,7 +1941,6 @@ Hostname=Host Invalid_setting=Ungültige_Einstellung Network=Netzwerk Please_specify_both_hostname_and_port=Bitte_geben_Sie_den_Namen_des_Hosts_und_den_Port_an -Port=Port Use_custom_proxy_configuration=Benutzerdefinierte_Proxy-Konfiguration_verwenden Clear_connection_settings=Verbindungseinstellungen_zurücksetzen Cleared_connection_settings.=Verbindungseinstellungen_wurden_zurückgesetzt. @@ -2055,9 +2034,6 @@ Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modif Added_entry=Eintrag_hinzugefügt Added_new_'%0'_entry.=Neuer_'%0'_Eintrag_hinzugefügt. Choose_the_URL_to_download.=Wählen_Sie_die_Download-URL -Could_not_connect_to_SQL_database_for_the_following_reason\:=Verbindung_zur_SQL-Datenbak_aus_folgendem_Grund_fehlgeschlagen\: -Could_not_export_to_SQL_database_for_the_following_reason\:=Export_in_die_SQL-Datenbak_aus_folgendem_Grund_fehlgeschlagen\: -Could_not_import_from_SQL_database_for_the_following_reason\:=Import_aus_der_SQL-Datenbak_aus_folgendem_Grund_fehlgeschlagen\: Deleted_entry=Eintrag_gelöscht Discard_changes=Änderungen_verwerfen @@ -2207,7 +2183,6 @@ abbreviation_detected=Abkürzung_erkannt Abbreviate_journal_names=Kürze_Zeitschriftentitel_ab Abbreviating...=Kürze_ab... Adding_fetched_entries=Füge_abgerufene_Einträge_hinzu -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?=Wollen_Sie_wirklich_die_bereits_bestehenden_SQL-Datenbanken_entfernen? Display_keywords_appearing_in_ALL_entries=Zeige_Schlüsselwörter_die_in_ALLEN_Einträgen_vorkommen Display_keywords_appearing_in_ANY_entry=Zeige_Schlüsselwörter_die_in_mind._EINEM_Eintrag_vorkommen Fetching_entries_from_Inspire=Rufe_Einträge_von_Inspire_ab @@ -2223,11 +2198,8 @@ Ill-formed_entrytype_comment_in_BIB_file=Fehlerhafter_Eintragstypkommentar_in_BI Clipboard=Zwischenablage Could_not_paste_entry_as_text\:=Konnte_Einträge_nicht_als_Text_parsen\: Do_you_still_want_to_continue?=Wollen_Sie_wirklich_fortfahren? -Please_enter_the_desired_name\:=Bitte_geben_Sie_den_gewünschten_Namen_ein\: -SQL_Export=SQL_Export This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:=Diese_Aktion_wird_jeweils_das/die_folgenden_Feld(er)_in_mindestens_einem_Eintrag_ändern\: This_could_cause_undesired_changes_to_your_entries.=Dies_könnte_ungewollte_Änderungen_an_Ihren_Einträgen_hervorrufen. -You_have_entered_an_invalid_or_already_existent_DB_name.=Sie_haben_einen_ungültigen_oder_bereits_existierenden_DB-Namen_eingegeben. Disable_highlight_groups_matching_entries=Keine_Gruppen_hervorheben @@ -2456,3 +2428,37 @@ Entry_from_%0=Eintrag_basierend_auf_%0 Merge_entry_with_%0_information=Eintrag_mit_%0-Informationen_zusammenführen Updated_entry_with_info_from_%0=Eintrag_wurde_mit_%0-Information_aktualisiert. +Connection=Verbindung +Host=Host +Port=Port +Database=Datenbank +User=Benutzer +Connect=Verbinden +Connection_error=Verbindungsfehler +Driver_error=Treiberfehler +Connection_to_%0_server_stablished.=Verbindung_zum_%0_Server hergestellt. +Required_field_"%0"_is_empty.=Erforederliches_Feld_"%0"_ist_leer. +Corrupt_shared_database_structure.=Die_Struktur_der_gemeinsam_genutzten_Datenbank_ist_beschädigt. +Integrity_check_failed._Fixing...=Integritätscheck_fehlgeschlagen._Repariere... + +%0_driver_not_available.=%0-Treiber_nicht_verfügbar. + +The_connection_to_the_server_has_been_determinated.=Verbindung_zum_Server_wurde_abgebrochen. + +Connection_lost.=Verbindung_verloren. +Reconnect=Neu_verbinden. +Work_offline=Offline_arbeiten. + +Working_offline.=Arbeite_offline. + +Update_refused.=Aktualisierung_verweigert. +Update_refused=Aktualisierung_verweigert +Local_entry=Lokaler_Eintrag +Shared_entry=Gemeinsam_genutzter_Eintrag +Update_could_not_be_performed_due_to_existing_change_conflicts.=Aktualisierung_konnte_wegen_Konflikten_nicht_durchgeführt_werden. +You_are_not_working_on_the_newest_version_of_BibEntry.=Sie_arbeiten_nicht_mit_der_neusten_Version_von_BibEntry. +Local_version\:_%0=Lokale_Version\:_%0 +Shared_version\:_%0=Gemainsam_genutzte_Version\:_%0 +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.=Bitte_führen_Sie_den_gemeinsam_genutzten_Eintrag_mit_Ihrem_zusammen_und_klicken_Sie_auf_"Einträge_zuammenführen",_um_das_Problem_zu_beheben. +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?=Durch_einen_Abbruch_dieser_Operation_werden_die_Änderungen_nicht_synchronisiert._Trotzdem_abbrechen? +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.=Der_Eintrag,_welchen_Sie_zur_Zeit_bearbeiten,_ist_auf_der_gemeinsam_genutzen_Datenbank_nicht_mehr_vorhanden._Klicken_Sie_auf_"Behalten",_um_den_Eintrag_wiederherzustellen. diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index fc9bea89dd32..660b8a6f4828 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -117,7 +117,6 @@ Assigned_1_entry_to_group_"%0".=Assigned_1_entry_to_group_"%0". Attach_URL=Attach_URL Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension. -Attempting_SQL_export...=Attempting_SQL_export... Auto=Auto @@ -244,8 +243,6 @@ Column_width=Column_width Command_line_id=Command_line_id -Connect=Connect -Connect_to_SQL_database=Connect_to_SQL_database Contained_in=Contained_in @@ -310,12 +307,13 @@ cut_entries=cut_entries cut_entry=cut_entry -Database=Database Database_encoding=Database_encoding Database_properties=Database_properties +Database_type=Database_type + Date_format=Date_format Default=Default @@ -482,7 +480,6 @@ Error_while_writing=Error_while_writing Error_writing_to_%0_file(s).=Error_writing_to_%0_file(s). -Establishing_SQL_connection...=Establishing_SQL_connection... Exceptions=Exceptions Existing_file=Existing_file @@ -504,7 +501,6 @@ Export_preferences_to_file=Export_preferences_to_file Export_properties=Export_properties Export_to_clipboard=Export_to_clipboard -Export_to_SQL_database=Export_to_SQL_database Exporting=Exporting Extension=Extension @@ -915,6 +911,8 @@ Open_file=Open_file Open_last_edited_databases_at_startup=Open_last_edited_databases_at_startup +Open_shared_database=Open_shared_database + Open_right-click_menu_with_Ctrl+left_button=Open_right-click_menu_with_Ctrl+left_button Open_terminal_here=Open_terminal_here @@ -987,7 +985,6 @@ Please_enter_the_string's_label=Please_enter_the_string's_label Please_select_an_importer.=Please_select_an_importer. Please_select_exactly_one_group_to_move.=Please_select_exactly_one_group_to_move. -Please_specify_the=Please_specify_the Possible_duplicate_entries=Possible_duplicate_entries @@ -1007,6 +1004,7 @@ Primary_sort_criterion=Primary_sort_criterion Problem_with_parsing_entry=Problem_with_parsing_entry Processing_%0=Processing_%0 Program_output=Program_output +Pull_changes_from_shared_database=Pull_changes_from_shared_database Pushed_citations_to_%0=Pushed_citations_to_%0 @@ -1165,8 +1163,6 @@ Select_new_ImportFormat_subclass=Select_new_ImportFormat_subclass Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Select_the_tree_nodes_to_view_and_accept_or_reject_changes Selected_entries=Selected_entries -Server_hostname=Server_hostname -Server_type=Server_type Set_field=Set_field Set_fields=Set_fields @@ -1235,7 +1231,6 @@ source_edit=source_edit Special_name_formatters=Special_name_formatters Special_table_columns=Special_table_columns -SQL_connection_established.=SQL_connection_established. Starting_import=Starting_import @@ -1452,8 +1447,6 @@ Fetching_Medline_by_id...=Fetching_Medline_by_id... Fetching_Medline_by_term...=Fetching_Medline_by_term... Please_enter_a_valid_number=Please_enter_a_valid_number Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms. -Connect_to_external_SQL_database=Connect_to_external_SQL_database -Export_to_external_SQL_database=Export_to_external_SQL_database Show_search_results_in_a_window=Show_search_results_in_a_window Move_file_to_file_directory?=Move_file_to_file_directory? Rename_to_'%0'=Rename_to_'%0' @@ -1467,7 +1460,6 @@ Unable_to_save_database=Unable_to_save_database BibTeX_key_generator=BibTeX_key_generator Unable_to_open_link.=Unable_to_open_link. -Attempting_SQL_import...=Attempting_SQL_import... Move_the_keyboard_focus_to_the_entry_table=Move_the_keyboard_focus_to_the_entry_table MIME_type=MIME_type @@ -1703,22 +1695,11 @@ Doing_a_cleanup_for_%0_entries...=Doing_a_cleanup_for_%0_entries... No_entry_needed_a_clean_up=No_entry_needed_a_clean_up One_entry_needed_a_clean_up=One_entry_needed_a_clean_up %0_entries_needed_a_clean_up=%0_entries_needed_a_clean_up -Error_importing_from_database=Error_importing_from_database Error_downloading_file_'%0'=Error_downloading_file_'%0' Download_failed=Download_failed -%0_databases_will_be_imported=%0_databases_will_be_imported -Importing_canceled=Importing_canceled -There_are_no_available_databases_to_be_imported=There_are_no_available_databases_to_be_imported -Import_from_SQL_database=Import_from_SQL_database -Imported_%0_databases_successfully=Imported_%0_databases_successfully -<_CREATE_NEW_DATABASE_>=<_CREATE_NEW_DATABASE_> Remove_selected=Remove_selected -SQL_Database_Exporter=SQL_Database_Exporter -Select_target_SQL_database\:=Select_target_SQL_database\: -SQL_Database_Importer=SQL_Database_Importer -Please_select_which_JabRef_databases_do_you_want_to_import\:=Please_select_which_JabRef_databases_do_you_want_to_import\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.=Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost. Attach_file=Attach_file @@ -1856,7 +1837,6 @@ Hostname=Hostname Invalid_setting=Invalid_setting Network=Network Please_specify_both_hostname_and_port=Please_specify_both_hostname_and_port -Port=Port Please_specify_both_username_and_password=Please_specify_both_username_and_password Use_custom_proxy_configuration=Use_custom_proxy_configuration @@ -1959,7 +1939,6 @@ Export_with_selected_format=Export_with_selected_format Return_to_JabRef=Return_to_JabRef Please_move_the_file_manually_and_link_in_place.=Please_move_the_file_manually_and_link_in_place. Could_not_connect_to_%0=Could_not_connect_to_%0 -Could_not_export_to_SQL_database_for_the_following_reason\:=Could_not_export_to_SQL_database_for_the_following_reason\: Warning\:_%0_out_of_%1_entries_have_undefined_BibTeX_key.=Warning\:_%0_out_of_%1_entries_have_undefined_BibTeX_key. occurrence=occurrence Added_new_'%0'_entry.=Added_new_'%0'_entry. @@ -1992,8 +1971,6 @@ unexpected_opening_curly_bracket=unexpected_opening_curly_bracket large_capitals_are_not_masked_using_curly_brackets_{}=large_capitals_are_not_masked_using_curly_brackets_{} should_contain_a_four_digit_number=should_contain_a_four_digit_number should_contain_a_valid_page_number_range=should_contain_a_valid_page_number_range -Could_not_connect_to_SQL_database_for_the_following_reason\:=Could_not_connect_to_SQL_database_for_the_following_reason\: -Could_not_import_from_SQL_database_for_the_following_reason\:=Could_not_import_from_SQL_database_for_the_following_reason\: Filled=Filled Field_is_missing=Field_is_missing Search_%0=Search_%0 @@ -2082,7 +2059,6 @@ wrong_entry_type_as_proceedings_has_page_numbers=wrong_entry_type_as_proceedings Abbreviate_journal_names=Abbreviate_journal_names Abbreviating...=Abbreviating... Adding_fetched_entries=Adding_fetched_entries -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?=Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs? Display_keywords_appearing_in_ALL_entries=Display_keywords_appearing_in_ALL_entries Display_keywords_appearing_in_ANY_entry=Display_keywords_appearing_in_ANY_entry Fetching_entries_from_Inspire=Fetching_entries_from_Inspire @@ -2102,11 +2078,8 @@ Move_linked_files_to_default_file_directory_%0=Move_linked_files_to_default_file Clipboard=Clipboard Could_not_paste_entry_as_text\:=Could_not_paste_entry_as_text\: Do_you_still_want_to_continue?=Do_you_still_want_to_continue? -Please_enter_the_desired_name\:=Please_enter_the_desired_name\: -SQL_Export=SQL_Export This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:=This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\: This_could_cause_undesired_changes_to_your_entries.=This_could_cause_undesired_changes_to_your_entries. -You_have_entered_an_invalid_or_already_existent_DB_name.=You_have_entered_an_invalid_or_already_existent_DB_name. Run_field_formatter\:=Run_field_formatter\: Table_font_size_is_%0=Table_font_size_is_%0 %0_import_canceled=%0_import_canceled @@ -2294,3 +2267,32 @@ No_%0_found=No_%0_found Entry_from_%0=Entry_from_%0 Merge_entry_with_%0_information=Merge_entry_with_%0_information Updated_entry_with_info_from_%0=Updated_entry_with_info_from_%0 +Connection=Connection +Host=Host +Port=Port +Database=Database +User=User +Connect=Connect +Connection_error=Connection_error +Driver_error=Driver_error +Connection_to_%0_server_stablished.=Connection_to_%0_server_stablished. +Required_field_"%0"_is_empty.=Required_field_"%0"_is_empty. +Corrupt_shared_database_structure.=Corrupt_shared_database_structure. +Integrity_check_failed._Fixing...=Integrity_check_failed._Fixing... +%0_driver_not_available.=%0_driver_not_available. +The_connection_to_the_server_has_been_determinated.=The_connection_to_the_server_has_been_determinated. +Connection_lost.=Connection_lost. +Reconnect=Reconnect +Work_offline=Work_offline +Working_offline.=Working_offline. +Update_refused.=Update_refused. +Update_refused=Update_refused +Local_entry=Local_entry +Shared_entry=Shared_entry +Update_could_not_be_performed_due_to_existing_change_conflicts.=Update_could_not_be_performed_due_to_existing_change_conflicts. +You_are_not_working_on_the_newest_version_of_BibEntry.=You_are_not_working_on_the_newest_version_of_BibEntry. +Local_version\:_%0=Local_version\:_%0 +Shared_version\:_%0=Shared_version\:_%0 +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.=Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem. +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?=Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway? +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.=The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry. diff --git a/src/main/resources/l10n/JabRef_es.properties b/src/main/resources/l10n/JabRef_es.properties index 5fe95c203e9a..19523762a8d8 100644 --- a/src/main/resources/l10n/JabRef_es.properties +++ b/src/main/resources/l10n/JabRef_es.properties @@ -64,7 +64,6 @@ Assigned_%0_entries_to_group_"%1".=Se_han_asignado_%0_entradas_al_grupo_"%1" Assigned_1_entry_to_group_"%0".=Asignada_una_entrada_al_grupo_"%0". Attach_URL=Adjuntar_URL Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Intentar_establecer_automáticamente_archivo_enlaces_para_sus_entradas._El_establecimiento_automático_funciona_si_un_archivo_en_la_carpeta_archivo_o_un_subdirectrio_
_tiene_nombre_idéntico_a_una_entrada_BibTeX_más_la_extensión. -Attempting_SQL_export...=Intentando_la_exportación_SQL... Auto=Auto Autodetect_format=Autodetectar_formato Autogenerate_BibTeX_keys=Autogenerar_claves_BibTeX @@ -130,7 +129,6 @@ Color_for_marking_incomplete_entries=Color_para_marcar_entradas_incompletas Column_width=Ancho_de_columna Command_line_id=Id_de_línea_de_comando Connect=Conectar -Connect_to_SQL_database=Conectar_a_base_de_datos_SQL Contained_in=Contenido_en Content=Contenido Copied=Copiado @@ -166,9 +164,10 @@ Customize_key_bindings=Pesonalizar_combinaciones_de_tecla Cut=Cortar cut_entries=Cortar_entradas cut_entry=Cortar_entrada -Database=Base_de_datos Database_encoding=Codificación_de_la_Base_de_Datos Database_properties=Propiedades_de_la_Base_de_Datos + +Database_type= Date_format=Formato_de_fecha Default=Por_defecto Default_encoding=Codificación_por_defecto @@ -257,7 +256,6 @@ Error_setting_field=Error_estableciendo_el_campo Error_while_downloading_file\:=Error_al_descargar_el_archivo\: Error_while_writing=Error_al_escribir Error_writing_to_%0_file(s).=Error_escribiendo_%0_archivo(s). -Establishing_SQL_connection...=Estableciendo_conexión_SQL... Exceptions=Excepciones Existing_file=Fichero_existente '%0'_exists._Overwrite_file?='%0'_existe._¿Sobreescribir? @@ -269,7 +267,6 @@ Export_preferences=Exportar_preferencias Export_preferences_to_file=Exportar_preferencias_a_archivo Export_properties=Exportar_propiedades Export_to_clipboard=Exportar_al_portapapeles -Export_to_SQL_database=Exportar_a_base_de_datos_SQL Exporting=Exportando Extension=Extensión External_changes=Cambios_externos @@ -488,6 +485,7 @@ Open_database=Abrir_base_de_datos Open_editor_when_a_new_entry_is_created=Abrir_editor_al_crear_nueva_entrada Open_file=Abrir_archivo Open_last_edited_databases_at_startup=Abrir_las_últimas_bases_de_datos_editadas_al_arrancar +Open_shared_database= Open_right-click_menu_with_Ctrl+left_button=Abrir_el_menú_contextual_con_Ctrl+Botón_izquierdo Open_terminal_here=Open_terminal_here Open_URL_or_DOI=Abril_URL_o_DOI @@ -526,7 +524,6 @@ Please_enter_the_field_to_search_(e.g._keywords)_and_the_keyword_to_searc Please_enter_the_string's_label=Introduzca_la_etiqueta_de_la_cadena Please_select_an_importer.=Seleccionar_un_importador. Please_select_exactly_one_group_to_move.=Seleccione_exactamente_un_grupo_para_mover. -Please_specify_the=Especifique_el Possible_duplicate_entries=Posibles_entradas_duplicadas Possible_duplicate_of_existing_entry._Click_to_resolve.=Posible_duplicado_de_entrada_existente._Clique_para_resolver. Preamble=Preámbulo @@ -539,6 +536,7 @@ Problem_with_parsing_entry=Problemas_analizando_entradas Processing_%0=Procesando_%0 Program_output=Salida_del_programa +Pull_changes_from_shared_database= Pushed_citations_to_%0=Se_han_enviado_las_citas_a_%0 Quit_JabRef=Salir_de_JabRef Quit_synchronization=Dejar_de_sincronizar @@ -621,8 +619,6 @@ Select_file_from_ZIP-archive=Seleccionar_archivo_desde_archivo_ZIP Select_new_ImportFormat_subclass=Seleccionar_nueva_subclase_de_formato_de_importación Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Seleccionar_nodos_de_árbol_para_ver_y_aceptar_o_rechazar_los_cambios. Selected_entries=Entradas_seleccionadas -Server_hostname=Nombre_de_host_del_servidor -Server_type=Tipo_de_servidor Set_field=Establecer_campo Set_fields=Establecer_campos Set_general_fields=Establecer_campos_generales @@ -660,7 +656,6 @@ Sorted_immediate_subgroups.=Subgrupos_inmediatos_ordenados. source_edit=editar_fuente Special_name_formatters=Formateadores_con_nombre_especial Special_table_columns=Columna_de_tabla_especiales -SQL_connection_established.=Conexión_SQL_establecida Starting_import=Comenzando_a_importar Statically_group_entries_by_manual_assignment=Agrupar_entradas_estadísticamente_mediante_asgnación_manual Status=Estado @@ -790,8 +785,6 @@ Fetching_Medline_by_term...=Recuperando_desde_Medline_por_término... %0_import_canceled=Importación_desde_%0_cancelada Please_enter_a_valid_number=Por_favor,_introduzca_un_número_válido Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=Por_favor,_introduzca_una_lista_de_valores_separados_por_coma_de_IDs_de_Medline_(números)_o_términos_de_búsqueda. -Connect_to_external_SQL_database=Conectar_a_base_de_datos_SQL_externa -Export_to_external_SQL_database=Exportar_a_base_de_datos_SQL_externa Show_search_results_in_a_window=Mostrar_los_resultados_de_la_búsqueda_en_una_ventana Move_file_to_file_directory?=¿Mover_archivo_a_la_carpeta_de_archivos? Rename_to_'%0'=Renombrar_a_'%0' @@ -803,7 +796,6 @@ Database_protection=Proteccion_de_la_base_de_datos Unable_to_save_database=No_es_posible_guardar_la_base_de_datos BibTeX_key_generator=Generador_de_claves_BibTeX Unable_to_open_link.=No_es_posible_abrir_el_enlace. -Attempting_SQL_import...=Intentando_importación_SQL Move_the_keyboard_focus_to_the_entry_table=Mover_el_foco_del_teclado_a_la_tabla_de_entradas MIME_type=Tipo_MIME This_feature_lets_new_files_be_opened_or_imported_into_an_already_running_instance_of_JabRef
instead_of_opening_a_new_instance._For_instance,_this_is_useful_when_you_open_a_file_in_JabRef
from_your_web_browser.
Note_that_this_will_prevent_you_from_running_more_than_one_instance_of_JabRef_at_a_time.=Esta_función_permite_que_nuevos_archivos_seasn_abiertos_o_importados_en_una_instancia_de_JabRef_que_ya_se_esté_ejecutando,
en_lugar_de_abrir_una_nueva_instancia._Esto_es_útil,_por_ejemplo,_cuando_se_abre_un_archivo_en_JabRef_desde_
_el_navegador_de_internet.
_Tenga_en_cuenta_que_esto_le_impedirá_ejecutar_más_de_una_instancia_de_JabRef_a_la_vez. @@ -1016,20 +1008,9 @@ Doing_a_cleanup_for_%0_entries...=Haciendo_la_limpieza_de_%0_entradas No_entry_needed_a_clean_up=Ninguna_entrada_necesitó_de_limpieza One_entry_needed_a_clean_up=Sólo_una_entrada_necesitó_limpieza %0_entries_needed_a_clean_up=%0_entradas_necesitarion_limpieza -Error_importing_from_database=Error_al_importar_desde_base_de_datos Error_downloading_file_'%0'=Error_descargando_el_archivo_'%0' Download_failed=Falló_la_descarga -%0_databases_will_be_imported=%0_bases_de_datos_serán_importadas -Importing_canceled=Importación_cancelada -There_are_no_available_databases_to_be_imported=No_hay_bases_de_datos_disaponibles_para_importar -Import_from_SQL_database=Importar_desde_base_de_datos_SQL -Imported_%0_databases_successfully=%0_bases_de_datos_importadas_con_éxito -<_CREATE_NEW_DATABASE_>=<_CREAR_NUEVA_BASE_DE_DATOS_> Remove_selected=Eliminar_seleccionados -SQL_Database_Exporter=Exportador_a_base_de_datos_SQL -Select_target_SQL_database\:=Seleccionar_base_de_datos_SQL_de_destino\: -SQL_Database_Importer=Importador_de_base_de_datos_SQL -Please_select_which_JabRef_databases_do_you_want_to_import\:=Por_favor,_selecciones_las_bases_de_datos_JabRef_que_desea_importar\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.=El_arbol_de_grupo_no_puede_ser_examinado._Si_guarda_la_base_de_datos_BibTeX,_todos_los_grupos_se_perderán. Attach_file=Adjuntar_archivo Setting_all_preferences_to_default_values.=Estableciendo_todas_las_preferencias_a_los_valores_por_defecto. @@ -1250,9 +1231,6 @@ Path_to_%0=Ruta_hasta_%0 Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modified_groups_tree.=Aceptar_el_cambio_reemplaza_el_árbol_completo_de_grupos_por_el_árbol_de_grupos_modificado_externamente Added_entry=Entrada_añadida Added_new_'%0'_entry.=Añadida_la_nueva_entrada_'%0' -Could_not_connect_to_SQL_database_for_the_following_reason\:=No_se_pudo_conectar_a_la_base_de_datos_SQL_debido_a -Could_not_export_to_SQL_database_for_the_following_reason\:=No_se_pudo_exportar_a_la_base_de_datos_SQL_debido_a -Could_not_import_from_SQL_database_for_the_following_reason\:=No_se_pudo_importar_desde_la_base_de_datos_SQL_debido_a Deleted_entry=Entrada_eliminada Discard_changes=Descartar_cambios Donate_to_JabRef=Donar_a_JabRef @@ -1398,7 +1376,6 @@ wrong_entry_type_as_proceedings_has_page_numbers=tipo_de_entrada_incorrecto,_ya_ Abbreviate_journal_names=Abreviar_nombres_de_publicación Abbreviating...=Abreviando... Adding_fetched_entries=Añadiendo_las_entradas_recuperadas -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?=¿Seguro_que_desea_eliminar_las_bases_de_datos_SQL_existentes? Display_keywords_appearing_in_ALL_entries=Mostrar_palabras_clave_que_aparezcan_en_TODAS_las_entradas Display_keywords_appearing_in_ANY_entry=Mostrar_palabras_clave_que_aparezcan_en_ALGUNA_entrada Fetching_entries_from_Inspire=Recuperando_entradas_desde_Inspire @@ -1414,11 +1391,8 @@ Ill-formed_entrytype_comment_in_BIB_file=Comentario_de_tipo_de_entrada_mal_forma Clipboard=Portapapeles Could_not_paste_entry_as_text\:=No_se_puede_pegar_la_entrada_como_texto\: Do_you_still_want_to_continue?=¿Aún_desea_continuar? -Please_enter_the_desired_name\:=Por_favor,_introduzca_el_nombre_deseado\: -SQL_Export=Exportar_a_SQL This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:=Esta_acción_modificará_los_siguientes_campos_en_al_menos_una_entrad_por_cada_uno\: This_could_cause_undesired_changes_to_your_entries.=Esto_podría_causar_cambios_indeseados_a_sus_entradas. -You_have_entered_an_invalid_or_already_existent_DB_name.=Ha_introducido_un_nombre_de_base_de_datos_inválido_o_inexistente. Disable_highlight_groups_matching_entries=Deshabilitar_el_resaltado_de_grupos_en_que_coincidad_entradas Run_field_formatter\:=Ejecutar_formateador_de_campo\: @@ -1643,3 +1617,35 @@ Entry_from_%0=Entrada_desde_%0 Merge_entry_with_%0_information=Fusionar_entrada_con_informaciçon_%0 Updated_entry_with_info_from_%0=Entrada_actualizada_con_información_del_%0 +Connection= +Host= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_fa.properties b/src/main/resources/l10n/JabRef_fa.properties index b60141cda090..77efbaa2376e 100644 --- a/src/main/resources/l10n/JabRef_fa.properties +++ b/src/main/resources/l10n/JabRef_fa.properties @@ -120,7 +120,6 @@ Assigned_1_entry_to_group_"%0".= Attach_URL= -Attempting_SQL_export...= Auto= @@ -248,8 +247,6 @@ Column_width= Command_line_id= -Connect= -Connect_to_SQL_database= Contained_in= @@ -314,12 +311,13 @@ cut_entries= cut_entry= -Database= Database_encoding= Database_properties= +Database_type= + Date_format= Default= @@ -489,7 +487,6 @@ Error_while_writing= Error_writing_to_%0_file(s).= -Establishing_SQL_connection...= Exceptions= Existing_file= @@ -511,7 +508,6 @@ Export_preferences_to_file= Export_properties= Export_to_clipboard= -Export_to_SQL_database= Exporting= Extension= @@ -926,6 +922,8 @@ Open_file= Open_last_edited_databases_at_startup= +Open_shared_database= + Open_right-click_menu_with_Ctrl+left_button= Open_terminal_here= @@ -998,7 +996,6 @@ Please_enter_the_string's_label= Please_select_an_importer.= Please_select_exactly_one_group_to_move.= -Please_specify_the= Possible_duplicate_entries= @@ -1017,6 +1014,7 @@ Primary_sort_criterion= Problem_with_parsing_entry= Processing_%0= Program_output= +Pull_changes_from_shared_database= Pushed_citations_to_%0= @@ -1198,8 +1196,6 @@ Select_new_ImportFormat_subclass= Select_the_tree_nodes_to_view_and_accept_or_reject_changes= Selected_entries= -Server_hostname= -Server_type= Set_field= Set_fields= @@ -1275,7 +1271,6 @@ source_edit= Special_name_formatters= Special_table_columns= -SQL_connection_established.= Starting_import= @@ -1505,8 +1500,6 @@ Fetching_Medline_by_term...= %0_import_canceled= Please_enter_a_valid_number= Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.= -Connect_to_external_SQL_database= -Export_to_external_SQL_database= Show_search_results_in_a_window= Move_file_to_file_directory?= @@ -1521,7 +1514,6 @@ Unable_to_save_database= BibTeX_key_generator= Unable_to_open_link.= -Attempting_SQL_import...= Move_the_keyboard_focus_to_the_entry_table= MIME_type= @@ -1758,22 +1750,11 @@ Doing_a_cleanup_for_%0_entries...= No_entry_needed_a_clean_up= One_entry_needed_a_clean_up= %0_entries_needed_a_clean_up= -Error_importing_from_database= Error_downloading_file_'%0'= Download_failed= -%0_databases_will_be_imported= -Importing_canceled= -There_are_no_available_databases_to_be_imported= -Import_from_SQL_database= -Imported_%0_databases_successfully= -<_CREATE_NEW_DATABASE_>= Remove_selected= -SQL_Database_Exporter= -Select_target_SQL_database\:= -SQL_Database_Importer= -Please_select_which_JabRef_databases_do_you_want_to_import\:= Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.= Attach_file= @@ -1911,7 +1892,6 @@ Hostname= Invalid_setting= Network= Please_specify_both_hostname_and_port= -Port= Use_custom_proxy_configuration= Clear_connection_settings= @@ -2005,9 +1985,6 @@ Added_new_'%0'_entry.= Changed_look_and_feel_settings= Clear_read_status= Convert_to_BibLatex_format_(for_example,_move_the_value_of_the_'journal'_field_to_'journaltitle')= -Could_not_connect_to_SQL_database_for_the_following_reason\:= -Could_not_export_to_SQL_database_for_the_following_reason\:= -Could_not_import_from_SQL_database_for_the_following_reason\:= Deleted_entry= Discard_changes= Donate_to_JabRef= @@ -2172,7 +2149,6 @@ wrong_entry_type_as_proceedings_has_page_numbers= Abbreviate_journal_names= Abbreviating...= Adding_fetched_entries= -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?= Display_keywords_appearing_in_ALL_entries= Display_keywords_appearing_in_ANY_entry= Fetching_entries_from_Inspire= @@ -2188,11 +2164,8 @@ Ill-formed_entrytype_comment_in_BIB_file= Clipboard= Could_not_paste_entry_as_text\:= Do_you_still_want_to_continue?= -Please_enter_the_desired_name\:= -SQL_Export= This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:= This_could_cause_undesired_changes_to_your_entries.= -You_have_entered_an_invalid_or_already_existent_DB_name.= Disable_highlight_groups_matching_entries= Run_field_formatter\:= @@ -2423,3 +2396,37 @@ Updated_entry_with_info_from_%0= Get_BibTeX_data_from_%0= No_%0_found= +Connection= +Host= +Port= +Database= +User= +Connect= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_fr.properties b/src/main/resources/l10n/JabRef_fr.properties index 542927e12470..0d7e263d733c 100644 --- a/src/main/resources/l10n/JabRef_fr.properties +++ b/src/main/resources/l10n/JabRef_fr.properties @@ -64,7 +64,6 @@ Assigned_%0_entries_to_group_"%1".=%0_entrées_ajoutées_au_groupe_"%1". Assigned_1_entry_to_group_"%0".=Une_entrée_ajoutée_au_groupe_"%0". Attach_URL=Attacher_l'URL Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Cela_tente_de_définir_automatiquement_les_liens_fichier_de_vos_entrées.
La_définition_automatique_fonctionne_si_un_fichier_dans_votre_répertoire_fichier
ou_dans_un_sous-répertoire_porte_le_même_nom_que_la_clef_d'une_entrée_BibTeX,
_l'extension_en_plus. -Attempting_SQL_export...=Tentative_d'exportation_SQL... Auto=Auto Autodetect_format=Détection_automatique_du_format Autogenerate_BibTeX_keys=Création_automatique_des_clefs_BibTeX @@ -130,7 +129,6 @@ Color_for_marking_incomplete_entries=Couleur_pour_marquer_les_entrées_incomplè Column_width=Largeur_de_colonne Command_line_id=Identifiant_de_la_ligne_de_commande Connect=Connexion_automatique -Connect_to_SQL_database=Se_connecter_à_une_base_SQL Contained_in=Contenu_dans Content=Contenu Copied=Copié @@ -166,9 +164,10 @@ Customize_key_bindings=Personnaliser_les_affectations_de_touches Cut=Couper cut_entries=Couper_les_entrées cut_entry=supprimer_l'entrée -Database=Base_de_données Database_encoding=Encodage_de_la_base_de_données Database_properties=Propriétés_de_la_base_de_données + +Database_type= Date_format=Format_de_date Default=Défaut Default_encoding=Encodage_par_défaut @@ -257,7 +256,6 @@ Error_setting_field=Erreur_de_configuration_du_champ Error_while_downloading_file\:=Erreur_lors_du_téléchargement_du_fichier_\: Error_while_writing=Erreur_lors_de_l'écriture Error_writing_to_%0_file(s).=Erreur_lors_de_l'écriture_de_%0_fichier(s). -Establishing_SQL_connection...=Etablissement_de_la_connexion_SQL... Exceptions=Exceptions Existing_file=Fichier_existant '%0'_exists._Overwrite_file?='%0'_existe._Ecraser_le_fichier_? @@ -269,7 +267,6 @@ Export_preferences=Exporter_les_préférences Export_preferences_to_file=Exporter_les_préférences_vers_un_fichier Export_properties=Propriétés_de_l'exportation Export_to_clipboard=Exporter_vers_le_presse-papiers -Export_to_SQL_database=Exporter_vers_une_base_SQL Exporting=Exportation_en_cours Extension=Extension External_changes=Modifications_externes @@ -488,6 +485,7 @@ Open_database=Ouvrir_une_base Open_editor_when_a_new_entry_is_created=Ouvrir_l'éditeur_quand_une_nouvelle_entrée_est_créée Open_file=Ouvrir_le_fichier Open_last_edited_databases_at_startup=Ouvrir_les_fichiers_de_la_dernière_session_au_démarrage +Open_shared_database= Open_right-click_menu_with_Ctrl+left_button=Dérouler_le_menu_contextuel_avec_Ctrl+clic_gauche Open_terminal_here=Ouvrir_un_terminal_ici Open_URL_or_DOI=Ouvrir_URL_ou_DOI @@ -526,7 +524,6 @@ Please_enter_the_field_to_search_(e.g._keywords)_and_the_keyword_to_searc Please_enter_the_string's_label=SVP,_entrez_le_nom_de_la_chaîne Please_select_an_importer.=Sélectionner_un_filtre_d'importation,_SVP. Please_select_exactly_one_group_to_move.=SVP,_sélectionnez_uniquement_un_groupe_à_déplacer. -Please_specify_the=Merci_de_spécifier_le Possible_duplicate_entries=Entrées_potentiellement_dupliquées Possible_duplicate_of_existing_entry._Click_to_resolve.=Duplication_possible_d'une_entrée_existante._Cliquer_pour_vérifier. Preamble=Préambule @@ -539,6 +536,7 @@ Problem_with_parsing_entry=Problème_de_traitement_d'une_entrée Processing_%0=Traitement_de_%0 Program_output=Sortie_du_programme +Pull_changes_from_shared_database= Pushed_citations_to_%0=Envoyer_les_citations_vers_%0 Quit_JabRef=Quitter_JabRef Quit_synchronization=Quitter_la_synchronisation @@ -621,8 +619,6 @@ Select_file_from_ZIP-archive=Sélectionner_un_fichier_depuis_une_archive_ZIP Select_new_ImportFormat_subclass=Sélectionner_une_nouvelle_sous-classe_ImportFormat Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Sélectionner_les_noeuds_de_l'arborescence_pour_voir,_et_accepter_ou_rejeter,_les_modifications Selected_entries=Les_entrées_sélectionnées -Server_hostname=Nom_de_l'hôte_du_serveur -Server_type=Type_de_serveur Set_field=Configurer_le_champ Set_fields=Configurer_les_champs Set_general_fields=Définir_les_champs_généraux @@ -660,7 +656,6 @@ Sorted_immediate_subgroups.=Sous-groupes_directs_triés. source_edit=édition_du_source Special_name_formatters=Formateurs_de_nom_spéciaux Special_table_columns=Colonnes_de_tableau_particulières -SQL_connection_established.=Connexion_SQL_établie. Starting_import=Début_d'importation Statically_group_entries_by_manual_assignment=Grouper_manuellement_les_entrées Status=Etat @@ -790,8 +785,6 @@ Fetching_Medline_by_term...=Recherche_sur_Medline_par_terme... %0_import_canceled=Importation_%0_annulée Please_enter_a_valid_number=SVP,_entrez_un_nombre_valide Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=SVP,_entrez_une_liste_séparée_par_des_virgules_d'ID_Medline_(nombres)_ou_de_termes_de_recherche. -Connect_to_external_SQL_database=Se_connecter_à_une_base_SQL_externe -Export_to_external_SQL_database=Exporter_vers_une_base_SQL_externe Show_search_results_in_a_window=Afficher_les_résultats_de_recherche_dans_une_fenêtre Move_file_to_file_directory?=Déplacer_le_fichier_vers_le_répertoire_de_fichiers_? Rename_to_'%0'=Renommer_vers_'%0' @@ -803,7 +796,6 @@ Database_protection=Protection_de_la_base Unable_to_save_database=Impossible_d'enregistrer_la_base BibTeX_key_generator=Générateur_de_clefs_BibTeX Unable_to_open_link.=Impossible_d'ouvrir_un_lien. -Attempting_SQL_import...=Tentative_d'importation_SQL... Move_the_keyboard_focus_to_the_entry_table=Déplacer_le_curseur_vers_la_table_des_entrées MIME_type=Type_MIME This_feature_lets_new_files_be_opened_or_imported_into_an_already_running_instance_of_JabRef
instead_of_opening_a_new_instance._For_instance,_this_is_useful_when_you_open_a_file_in_JabRef
from_your_web_browser.
Note_that_this_will_prevent_you_from_running_more_than_one_instance_of_JabRef_at_a_time.=Cette_fonction_permet_aux_nouveaux_fichiers_d'être_ouverts_ou_importés_dans_une_fenêtre_JabRef_déjà_active
au_lieu_d'ouvrir_une_nouvelle_fenêtre._Par_exemple,_c'est_utile_quand_vous_ouvrez_un_fichier_dans_JabRef
à_partir_de_notre_navigateur_internet._
Notez_que_cela_vous_empêchera_de_lancer_plus_d'une_fenêtre_JabRef_à_la_fois. @@ -1040,23 +1032,12 @@ Doing_a_cleanup_for_%0_entries...=Nettoyage_en_cours_pour_%0_entrées... No_entry_needed_a_clean_up=Aucune_entrée_ne_nécessitait_un_nettoyage One_entry_needed_a_clean_up=Une_entrée_nécessitait_un_nettoyage %0_entries_needed_a_clean_up=%0_entrées_nécessitaient_un_nettoyage -Error_importing_from_database=Erreur_d'importation_à_partir_de_la_base_de_données %0_mode=Mode_%0 Error_downloading_file_'%0'=Erreur_lors_du_téléchargement_du_fichier_'%0' Download_failed=Echec_du_téléchargement -%0_databases_will_be_imported=%0_bases_de_données_seront_importées -Importing_canceled=Importation_annulée -There_are_no_available_databases_to_be_imported=Il_n'y_a_pas_de_base_de_données_à_importer -Import_from_SQL_database=Importer_à_partir_d'une_base_SQL -Imported_%0_databases_successfully=%0_bases_ont_été_importées_avec_succès -<_CREATE_NEW_DATABASE_>=<_CREER_UNE_NOUVELLE_BASE_> Remove_selected=Supprimer_la_sélection -SQL_Database_Exporter=Exporteur_de_base_SQL -Select_target_SQL_database\:=Sélectionner_la_base_SQL_cible_\: -SQL_Database_Importer=Importeur_de_base_SQL -Please_select_which_JabRef_databases_do_you_want_to_import\:=SVP,_sélectionnez_les_bases_JabRef_à_importer_\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.=L'arbre_des_groupes_n'a_pu_être_analysé._Si_vous_sauvez_la_base_BibTeX,_tous_les_groupes_seront_perdus. Attach_file=Attacher_un_fichier @@ -1196,7 +1177,6 @@ Hostname=Ordinateur_hôte Invalid_setting=Paramétrage_invalide Network=Réseau Please_specify_both_hostname_and_port=Entrez_à_la_fois_le_nom_de_l'ordinateur_hôte_et_le_port,_s'il_vous_plait -Port=Port Use_custom_proxy_configuration=Utiliser_une_configuration_de_proxy_personnalisée Clear_connection_settings=Réinitialiser_les_paramètres_de_connexion Cleared_connection_settings.=Paramètres_de_connexion_réinitialisés. @@ -1289,9 +1269,6 @@ Could_not_connect_to_%0=La_connexion_à_%0_n'a_pas_pu_être_établie Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modified_groups_tree.=Acceptation_que_l'arbre_des_groupes_entier_soit_remplacé_par_l'arbre_des_groupes_modifiés_externalement. Added_entry=Entrée_ajoutée Added_new_'%0'_entry.=Nouvelle_entrée_'%0'_ajoutée -Could_not_connect_to_SQL_database_for_the_following_reason\:=La_connexion_à_la_base_de_données_SQL_a_échouée_pour_la_raison_suivante_\: -Could_not_export_to_SQL_database_for_the_following_reason\:=L'exportation_vers_la_base_de_données_SQL_a_échouée_pour_la_raison_suivante_\: -Could_not_import_from_SQL_database_for_the_following_reason\:=L'importation_depuis_la_base_de_données_SQL_a_échouée_pour_la_raison_suivante_\: Deleted_entry=Entrée_supprimée Discard_changes=Abandonner_les_modifications Donate_to_JabRef=Faire_un_don_à_JabRef @@ -1450,7 +1427,6 @@ wrong_entry_type_as_proceedings_has_page_numbers=Mauvais_type_d'entrée_car_le_p Abbreviate_journal_names=Abrégez_les_noms_de_journaux Abbreviating...=Abrègement_en_cours Adding_fetched_entries=Ajout_des_entrées_récupérées -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?=Etes-vous_sûr_de_vouloir_supprimer_une_base_SQL_existante_? Display_keywords_appearing_in_ALL_entries=Afficher_les_mots-clefs_apparaissant_dans_TOUTES_les_entrées Display_keywords_appearing_in_ANY_entry=Afficher_les_mots-clefs_apparaissant_dans_AU_MOINS_UNE_entrée Fetching_entries_from_Inspire=Recherche_d'entrées_depuis_Inspire @@ -1466,11 +1442,8 @@ Ill-formed_entrytype_comment_in_BIB_file=Commentaire_de_type_d'entrées_mal_cons Clipboard=Presse-papiers Could_not_paste_entry_as_text\:=L'entrée_n'a_pas_pu_être_collée_comme_du_texte_: Do_you_still_want_to_continue?=Voulez-vous_continuer_? -Please_enter_the_desired_name\:=Entrez,_s'il_vous_plaît,_le_nom_souhaité_: -SQL_Export=Exportation_SQL This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:=Cette_action_modifiera_le(s)_champ(s)_suivant(s)_dans_au_moins_une_entrée_chacune_: This_could_cause_undesired_changes_to_your_entries.=Cela_pourrai_causer_des_changements_non_souhaités_dans_vos_entrées. -You_have_entered_an_invalid_or_already_existent_DB_name.=Vous_avez_entré_un_nom_de_base_invalide_ou_déjà_existant. Disable_highlight_groups_matching_entries=Désactiver_le_surlignement_des_groupes_correspondant_à_des_entrées @@ -1684,3 +1657,36 @@ Entry_from_%0=Entrée_à_partir_du_%0 Merge_entry_with_%0_information=Fusionner_l'entrée_sur_la_base_des_informations_du_%0 Updated_entry_with_info_from_%0=Entrée_mise_à_jour_à_partir_des_informations_du_%0 +Connection= +Host= +Port= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_in.properties b/src/main/resources/l10n/JabRef_in.properties index 6d4d4cd34de0..eb1073cb0f6a 100644 --- a/src/main/resources/l10n/JabRef_in.properties +++ b/src/main/resources/l10n/JabRef_in.properties @@ -64,7 +64,6 @@ Assigned_%0_entries_to_group_"%1".=Diterapkan_%0_entri_ke_grup_"%1". Assigned_1_entry_to_group_"%0".=Diterapkan_1_entri_ke_grup_"%0". Attach_URL=Lampirkan_URL Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Mencoba_atur_otomatis_berkas_tautan_untuk_entri_anda._Pengaturan_otomatis_berfungsi_jika_berkas_di_folder_berkas_atau_subfolder
diberi_nama_sama_dengan_kunci_BibTeX,_tambah_ekstensi. -Attempting_SQL_export...=Seang_proses_ekspor_SQL... Auto=Otomatis Autodetect_format=Deteksi_format_otomatis Autogenerate_BibTeX_keys=Kunci_BibTeX_dibuat_otomatis @@ -130,7 +129,6 @@ Color_for_marking_incomplete_entries=Tanda_untuk_entri_kosong Column_width=Lebar_kolom Command_line_id=id_perintah_baris Connect=Menghubungi -Connect_to_SQL_database=Menghubungi_basisdata_SQL Contained_in=Terkandung_di Content=Isi Copied=Disalin @@ -166,9 +164,10 @@ Customize_key_bindings=Ubahsuai_kunci_gabungan Cut=Potong cut_entries=potong_entri cut_entry=potong_entri -Database=Basisdata Database_encoding=Enkoding_basisdata Database_properties=Properti_basisdata + +Database_type= Date_format=Format_tanggal Default=Bawaan Default_encoding=Enkoding_bawaan @@ -257,7 +256,6 @@ Error_setting_field=Kesalahan_pengaturan_bidang Error_while_downloading_file\:=Kesalahan_ketika_muaturun_berkas\: Error_while_writing=Kesalahan_ketika_menulis Error_writing_to_%0_file(s).=Kesalahan_menulis_ke_berkas_%0. -Establishing_SQL_connection...=Sedang_membuat_koneksi_SQL... Exceptions=Perkecualian Existing_file=Berkas_yang_ada '%0'_exists._Overwrite_file?='%0'_esudah_ada._Berkas_ditindih? @@ -269,7 +267,6 @@ Export_preferences=Preferensi_Ekspor Export_preferences_to_file=Ekspor_preferensi_ke_berkas Export_properties=Ekspor_properti Export_to_clipboard=Ekspor_ke_papan_klip -Export_to_SQL_database=Ekspor_ke_basisdata_eksternal Exporting=Proses_mengekspor Extension=Ekstensi External_changes=Perubahan_eksternal @@ -487,6 +484,7 @@ Open_database=Buka_basisdata Open_editor_when_a_new_entry_is_created=Buka_penyunting_ketika_entri_baru_dibuat Open_file=Buka_berkas Open_last_edited_databases_at_startup=Buka_basisdata_terakhir_ketika_memulai +Open_shared_database= Open_right-click_menu_with_Ctrl+left_button=Buka_menu_klik_kanan_dengan_tombol_Ctrl+kiri Open_terminal_here=Buka_terminal_di_sini Open_URL_or_DOI=Buka_URL_atau_DOI @@ -525,7 +523,6 @@ Please_enter_the_field_to_search_(e.g._keywords)_and_the_keyword_to_searc Please_enter_the_string's_label=Tuliskan_label_string Please_select_an_importer.=Silahkan_pilih_pengimpor. Please_select_exactly_one_group_to_move.=Silahkan_pilih_satu_grup_yang_akan_dipindah. -Please_specify_the=Silahkan_nyatakan Possible_duplicate_entries=Mungkin_entri_sama Possible_duplicate_of_existing_entry._Click_to_resolve.=Mungkin_entri_sama_dengan_lainnya._Klik_untuk_menyelesaikan. Preamble=Preambel @@ -538,6 +535,7 @@ Problem_with_parsing_entry=Permasalahan_dengan_penguraian_entri Processing_%0=Memroses_%0 Program_output=Keluaran_program +Pull_changes_from_shared_database= Pushed_citations_to_%0=Kirim_acuan_ke_%0 Quit_JabRef=Keluar_JabRef Quit_synchronization=Keluar_sinkronisasi @@ -620,8 +618,6 @@ Select_file_from_ZIP-archive=Pilih_berkas_dari_arsip_ZIP Select_new_ImportFormat_subclass=Pilih_ImportFormat_subclass_baru Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Pilih_tiga_nodal_untuk_melihat,_menerima_atau_menolak_perubahan Selected_entries=Entri_pilihan -Server_hostname=Nama_Host_Server -Server_type=Tipe_Server Set_field=Pilih_bidang Set_fields=Pilih_beberapa_bidang Set_general_fields=Pilih_bidang_umum @@ -659,7 +655,6 @@ Sorted_immediate_subgroups.=Diurutkan_sub-grup_seketika. source_edit=sunting_sumber Special_name_formatters=Pemformat_Nama_Spesial Special_table_columns=Kolom_tabe_khusus -SQL_connection_established.=Koneksi_SQL_diadakan. Starting_import=Memulai_impor Statically_group_entries_by_manual_assignment=Entri_grup_statik_secara_penerapan_manual Status=Status @@ -789,8 +784,6 @@ Fetching_Medline_by_term...=Mengambil_Medline_berdasar_istilah... %0_import_canceled=Impor_%0_dibatalkan Please_enter_a_valid_number=Tuliskan_nomor_yang_benar Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=Gunakan_pemisah_koma_dari_ID_Medline_(angka)_atau_cari_istilah. -Connect_to_external_SQL_database=Menghubungi_basis_data_SQL_eksternal -Export_to_external_SQL_database=Ekspor_ke_basisdata_SQL_eksternal Show_search_results_in_a_window=Tampilkan_hasil_pencarian_di_jendela Move_file_to_file_directory?=Pindah_berkas_ke_direktori_berkas? Rename_to_'%0'=Ganti_nama_menjadi_'%0' @@ -802,7 +795,6 @@ Database_protection=Perlindungan_basisdata Unable_to_save_database=Tidak_bisa_menyimpan_basisdata BibTeX_key_generator=Pembuat_kunci_BibTeX Unable_to_open_link.=Tidak_bisa_membuka_tautan. -Attempting_SQL_import...=Mencoba_impor_SQL... Move_the_keyboard_focus_to_the_entry_table=Pindah_fokus_papanketik_ke_tabel_entri MIME_type=Tipe_MIME This_feature_lets_new_files_be_opened_or_imported_into_an_already_running_instance_of_JabRef
instead_of_opening_a_new_instance._For_instance,_this_is_useful_when_you_open_a_file_in_JabRef
from_your_web_browser.
Note_that_this_will_prevent_you_from_running_more_than_one_instance_of_JabRef_at_a_time.=Fitur_ini_memungkinkan_berkas_baru_atau_impor_ke_jendela_JabRef_yang_aktif
bukan_membuat_baru._Hal_ini_berguna_ketika_anda_membuka_berkas_di_JabRef
dari_halaman_web.
Hal_ini_akan_menghindari_anda_membuka_beberapa_JabRef_pada_saat_yang_sama. @@ -1020,21 +1012,10 @@ Doing_a_cleanup_for_%0_entries...=Melakukan_pembersihan_%0_entri... No_entry_needed_a_clean_up=Tidak_ada_entri_yang_membutuhkan_pembersihan One_entry_needed_a_clean_up=Sebuah_entri_membutuhkan_pembersihan %0_entries_needed_a_clean_up=%0_entri_membutuhkan_pembersihan -Error_importing_from_database=Kesalahan_ketika_mengimpor_dari_basisdata %0_mode=Mode_%0 Error_downloading_file_'%0'=Kesalahan_ketika_muatturun_berkas_'%0' Download_failed=Pemuatturunan_gagal -%0_databases_will_be_imported=%0_basisdata_akan_diimpor -Importing_canceled=Impor_dibatalkan -There_are_no_available_databases_to_be_imported=Tidak_ada_basisdata_yang_nyata_untuk_diimpor -Import_from_SQL_database=Impor_dari_basisdata_SQL -Imported_%0_databases_successfully=Impor_%0_basisdata_berhasil -<_CREATE_NEW_DATABASE_>=<_BUAT_BASISDATA_YANG_BARU_> -SQL_Database_Exporter=Fungsi_Ekspor_Basisdata_SQL -Select_target_SQL_database\:=Pilih_basisdata_SQL_target\: -SQL_Database_Importer=Fungsi_Impor_Basisdata_SQL -Please_select_which_JabRef_databases_do_you_want_to_import\:=Silahkan_memilih_basisdata_JabRef_untuk_diimpor\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.= Attach_file=Lampirkan_berkas @@ -1174,7 +1155,6 @@ Hostname=Nama_host Invalid_setting=Pengaturan_tidak_sah Network=Jaringan Please_specify_both_hostname_and_port=Silahkan_nyatakan_nama_host_dan_port -Port=Port Use_custom_proxy_configuration=Gunakan_konfigurasi_proxy_suaian Clear_connection_settings=Bersihkan_pengaturan_koneksi Cleared_connection_settings.=Pengaturan_koneksi_dibersihkan. @@ -1266,9 +1246,6 @@ Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modif Added_entry=Entri_ditambahkan Added_new_'%0'_entry.=Entri_yang_baru_'%0'_ditambahkan. Copy_BibTeX_key_and_title=Salin_kunci_BibTeX_dan_judul -Could_not_connect_to_SQL_database_for_the_following_reason\:= -Could_not_export_to_SQL_database_for_the_following_reason\:= -Could_not_import_from_SQL_database_for_the_following_reason\:= Deleted_entry=Entri_dihapus Discard_changes=Perubahan_dibuang Donate_to_JabRef=Sumbang_ke_JabRef @@ -1421,7 +1398,6 @@ wrong_entry_type_as_proceedings_has_page_numbers= Abbreviate_journal_names=Singkatkan_nama_jurnal Abbreviating...=Singkatkan... Adding_fetched_entries=Tambah_entri_ambilan -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?= Display_keywords_appearing_in_ALL_entries=Tampilkan_katakunci_yang_ada_dalam_semua_entri Display_keywords_appearing_in_ANY_entry= Fetching_entries_from_Inspire= @@ -1437,11 +1413,8 @@ Ill-formed_entrytype_comment_in_BIB_file= Clipboard= Could_not_paste_entry_as_text\:=Entri_tidak_bisa_dimuat_sebagai_teks\: Do_you_still_want_to_continue?=Apa_masih_mau_meneruskan? -Please_enter_the_desired_name\:= -SQL_Export=Ekspor_SQL This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:= This_could_cause_undesired_changes_to_your_entries.= -You_have_entered_an_invalid_or_already_existent_DB_name.= Disable_highlight_groups_matching_entries= Run_field_formatter\:= @@ -1660,3 +1633,36 @@ Entry_from_%0=Entri_dari_%0 Merge_entry_with_%0_information=Gabung_entri_dengan_informasi_%0 Updated_entry_with_info_from_%0=Entri_diperbarui_dengan_informasi_dari_%0 +Connection= +Host= +Port= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_it.properties b/src/main/resources/l10n/JabRef_it.properties index bdc0c444b48e..aa140234cd30 100644 --- a/src/main/resources/l10n/JabRef_it.properties +++ b/src/main/resources/l10n/JabRef_it.properties @@ -64,7 +64,6 @@ Assigned_%0_entries_to_group_"%1".=Assegnate_%0_voci_al_gruppo_"%1". Assigned_1_entry_to_group_"%0".=Una_voce_assegnata_al_gruppo_"%0". Attach_URL=Allega_URL Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Tentativo_di_definire_automaticamente_file_collegamenti_per_le_tue_voci._La_definizione_avviene_automaticamente_se_un_file_nella_cartella_file_o_sottocartella
ha_lo_stesso_nome_della_chiave_di_una_voce_BibTeX,_a_meno_dell'estensione. -Attempting_SQL_export...=Tentativo_di_esportazione_SQL... Auto=Auto Autodetect_format=Rivelamento_automatico_del_formato Autogenerate_BibTeX_keys=Generazione_automatica_delle_chiavi_BibTeX @@ -137,7 +136,6 @@ Column_width=Larghezza_della_colonna Command_line_id=Identificativo_della_riga_di_comando # Not sure\: "Registrazione completa" ? Connect=Connessione -Connect_to_SQL_database=Connessione_ad_un_database_SQL Contained_in=Contenuto_in Content=Contenuto Copied=Copiato @@ -175,9 +173,10 @@ Customize_key_bindings=Personalizza_combinazioni_di_tasti Cut=Taglia cut_entries=Taglia_voci cut_entry=taglia_voce -Database=Database Database_encoding=Codifica_database Database_properties=Propriet\u00e0_del_database + +Database_type= Date_format=Formato_data Default=Predefinito Default_encoding=Codifica_predefinita @@ -274,7 +273,6 @@ Error_while_downloading_file\:=Errore_nel_corso_del_download_del_file\: Error_while_writing=Errore_durante_la_scrittura Error_writing_to_%0_file(s).=Errore_di_scrittura_di_%0_file. -Establishing_SQL_connection...=Connessione_SQL_in_corso... Exceptions=Eccezioni Existing_file=File_esistente '%0'_exists._Overwrite_file?='%0'_esiste._Sovrascrivere_il_file? @@ -286,7 +284,6 @@ Export_preferences=Esporta_preferenze Export_preferences_to_file=Esporta_preferenze_in_un_file Export_properties=Esporta_propriet\u00e0 Export_to_clipboard=Esporta_negli_appunti -Export_to_SQL_database=Esporta_in_un_database_SQL Exporting=Esportazione_in_corso Extension=Estensione External_changes=Modifiche_esterne @@ -526,6 +523,7 @@ Open_database=Apri_database Open_editor_when_a_new_entry_is_created=Apri_per_modifiche_quando_una_nuova_voce_viene_creata Open_file=Apri_file Open_last_edited_databases_at_startup=All'avvio_apri_i_database_aperti_nella_sessione_precedente +Open_shared_database= Open_right-click_menu_with_Ctrl+left_button=Mostra_il_menu_contestuale_con_Ctrl_+_pulsante_sinistro Open_terminal_here= Open_URL_or_DOI=Apri_URL_o_DOI @@ -573,7 +571,6 @@ Please_enter_the_field_to_search_(e.g._keywords)_and_the_keyword_to_searc Please_enter_the_string's_label=Immettere_l'etichetta_della_stringa Please_select_an_importer.=Selezionare_un_filtro_di_importazione. Please_select_exactly_one_group_to_move.=Selezionare_un_solo_gruppo_da_spostare. -Please_specify_the=Specificare_il Possible_duplicate_entries=Voci_potenzialmente_duplicate Possible_duplicate_of_existing_entry._Click_to_resolve.=Possibile_duplicazione_di_una_voce_esistente._Cliccare_per_effettuare_la_verifica. Preamble=Preambolo @@ -586,6 +583,7 @@ Problem_with_parsing_entry=Problema_di_analisi_di_una_voce Processing_%0=Elaborazione_di_%0 Program_output=Output_del_programma +Pull_changes_from_shared_database= Pushed_citations_to_%0=Citazioni_inviate_a_%0 Quit_JabRef=Chiudi_JabRef Quit_synchronization=Chiudi_sincronizzazione @@ -676,8 +674,6 @@ Select_file_from_ZIP-archive=Seleziona_un_file_da_un_archivio_ZIP Select_new_ImportFormat_subclass=Seleziona_una_nuova_sottoclasse_ImportFormat Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Selezionare_i_nodi_dell'albero_per_vedere_ed_accettare_o_rifiutare_le_modifiche Selected_entries=Voci_selezionate -Server_hostname=Hostname_del_server -Server_type=Tipo_di_server Set_field=Imposta_il_campo Set_fields=Imposta_i_campi @@ -720,7 +716,6 @@ Sorted_immediate_subgroups.=Ordinati_i_sottogruppi_immediati. source_edit=modifica_sorgente Special_name_formatters=Formattazioni_speciali_dei_nomi Special_table_columns=Colonne_di_tabella_speciali -SQL_connection_established.=Connessione_SQL_stabilita. Starting_import=Inizio_importazione Statically_group_entries_by_manual_assignment=Raggruppa_manualmente_le_voci Status=Stato @@ -862,8 +857,6 @@ Fetching_Medline_by_term...=Recupero_da_Medline_per_termine... %0_import_canceled=Importazione_da_%0_annullata Please_enter_a_valid_number=Inserire_un_numero_valido Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=Inserire_una_lista_separata_da_virgole_di_ID_Medline_(numeri)_o_termini_di_ricerca. -Connect_to_external_SQL_database=Connessione_ad_un_database_SQL_esterno -Export_to_external_SQL_database=Esportazione_su_un_database_SQL_esterno Show_search_results_in_a_window=Mostra_i_risultati_della_ricerca_in_una_finestra Move_file_to_file_directory?=Spostare_i_file_nella_cartella_dei_file_principale? @@ -878,7 +871,6 @@ Database_protection=Protezione_del_database Unable_to_save_database=Impossibile_salvare_il_database BibTeX_key_generator=Generatore_di_chiavi_BibTeX Unable_to_open_link.=Impossibile_aprire_il_collegamento. -Attempting_SQL_import...=Tentativo_di_importazione_SQL... Move_the_keyboard_focus_to_the_entry_table=Sposta_il_cursore_nella_tabella_delle_voci MIME_type=Tipo_MIME @@ -1116,22 +1108,11 @@ Doing_a_cleanup_for_%0_entries...=Ripulitura_per_%0_voci... No_entry_needed_a_clean_up=Nessuna_voce_necessita_ripulitura One_entry_needed_a_clean_up=Una_voce_necessita_ripulitura %0_entries_needed_a_clean_up=%0_voci_necessitano_ripulitura -Error_importing_from_database=Errore_di_importazione_dal_database %0_mode=modalit\u00e0_%0 Error_downloading_file_'%0'=Errore_nel_corso_del_download_del_file_'%0' Download_failed=Download_fallito -%0_databases_will_be_imported=%0_database_saranno_importati -Importing_canceled=Importazione_annullata -There_are_no_available_databases_to_be_imported=Non_sono_disponibili_database_da_importare -Import_from_SQL_database=Importa_da_un_database_SQL -Imported_%0_databases_successfully=Importati_con_successo_%0_database -<_CREATE_NEW_DATABASE_>=<_CREA_NUOVO_DATABASE_> Remove_selected=Rimuovi_la_selezione -SQL_Database_Exporter=Esportazione_database_SQL -Select_target_SQL_database\:=Seleziona_il_database_SQL_di_destinazione\: -SQL_Database_Importer=Importazione_database_SQL -Please_select_which_JabRef_databases_do_you_want_to_import\:=Selezionare_il_database_JabRef_da_importare\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.=Impossibile_analizzare_l'albero_dei_gruppi._Salvando_il_database_BibTeX_i_gruppi_saranno_persi. Attach_file=Allega_file @@ -1273,7 +1254,6 @@ Hostname=Host Invalid_setting=Impostazione_non_valida Network=Rete Please_specify_both_hostname_and_port=Specificare_sia_il_nome_dell'host_sia_il_numero_della_porta -Port=Porta Use_custom_proxy_configuration=Utilizza_una_configurazione_del_proxy_personalizzata. Clear_connection_settings=Reinizializza_i_parametri_di_connessione Cleared_connection_settings.=Parametri_di_connessione_reinizializzati @@ -1366,9 +1346,6 @@ Could_not_connect_to_%0=Impossibile_la_connessione_a_%0 Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modified_groups_tree.= Added_entry= Added_new_'%0'_entry.= -Could_not_connect_to_SQL_database_for_the_following_reason\:= -Could_not_export_to_SQL_database_for_the_following_reason\:= -Could_not_import_from_SQL_database_for_the_following_reason\:= Deleted_entry= Discard_changes= Donate_to_JabRef= @@ -1516,7 +1493,6 @@ wrong_entry_type_as_proceedings_has_page_numbers= Abbreviate_journal_names= Abbreviating...= Adding_fetched_entries= -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?= Display_keywords_appearing_in_ALL_entries= Display_keywords_appearing_in_ANY_entry= Fetching_entries_from_Inspire= @@ -1532,11 +1508,8 @@ Ill-formed_entrytype_comment_in_BIB_file= Clipboard= Could_not_paste_entry_as_text\:= Do_you_still_want_to_continue?= -Please_enter_the_desired_name\:= -SQL_Export= This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:= This_could_cause_undesired_changes_to_your_entries.= -You_have_entered_an_invalid_or_already_existent_DB_name.= Disable_highlight_groups_matching_entries= Run_field_formatter\:= @@ -1760,3 +1733,36 @@ Entry_from_%0= Merge_entry_with_%0_information= Updated_entry_with_info_from_%0= +Connection= +Host= +Port= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_ja.properties b/src/main/resources/l10n/JabRef_ja.properties index 15f048bc038c..177cf5739714 100644 --- a/src/main/resources/l10n/JabRef_ja.properties +++ b/src/main/resources/l10n/JabRef_ja.properties @@ -121,7 +121,6 @@ Assigned_1_entry_to_group_"%0".=1項目をグループ「%1」に割り当てま Attach_URL=URLを添付 Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=これは、使用中の項目のファイルリンクを自動設定します。自動設定は、ファイルディレクトリないし下層ディレクトリの
ファイルが、項目のBibTeX鍵と同一名+拡張子として命名されているときのみ、機能します。 -Attempting_SQL_export...=SQLへの書き出しを試みています... Auto=自動 @@ -250,7 +249,6 @@ Column_width=列幅 Command_line_id=コマンドラインID Connect=接続 -Connect_to_SQL_database=SQLデータベースに接続 Contained_in=所在 @@ -315,12 +313,13 @@ cut_entries=項目を切り取り cut_entry=項目を切り取り -Database=データベース Database_encoding=データベースのエンコーディング Database_properties=データベース特性 +Database_type= + Date_format=日付書式 Default=既定値 @@ -490,7 +489,6 @@ Error_while_writing=書き込み中にエラー発生 Error_writing_to_%0_file(s).=%0ファイルを書き込み中にエラー発生 -Establishing_SQL_connection...=SQL接続を確立しています... Exceptions=例外 Existing_file=既存ファイル @@ -512,7 +510,6 @@ Export_preferences_to_file=設定をファイルに書き出す Export_properties=特性を書き出す Export_to_clipboard=クリップボードに書き出す -Export_to_SQL_database=SQLデータベースに書き出す Exporting=書き出し中 Extension=拡張子 @@ -942,6 +939,8 @@ Open_file=ファイルを開く Open_last_edited_databases_at_startup=起動時に最後に編集していたデータベースを開く +Open_shared_database= + Open_right-click_menu_with_Ctrl+left_button=Ctrl+左ボタンで右クリックメニューを開く Open_terminal_here=ここで端末を開く @@ -1014,7 +1013,6 @@ Please_enter_the_string's_label=文字列のラベルを入力してください Please_select_an_importer.=読込を選択してください。 Please_select_exactly_one_group_to_move.=移動するグループを一つだけ選択してください。 -Please_specify_the=以下を指定してください\: Possible_duplicate_entries=潜在的な重複項目 @@ -1036,6 +1034,7 @@ Problem_with_parsing_entry=項目を解析中に問題が発生しました Processing_%0=以下を処理中です\:_%0 Program_output=プログラム出力 +Pull_changes_from_shared_database= Pushed_citations_to_%0=引用を%0に送り込みました @@ -1217,8 +1216,6 @@ Select_new_ImportFormat_subclass=新しいImportFormatサブクラスを選択 Select_the_tree_nodes_to_view_and_accept_or_reject_changes=ツリーノードを選択して表示させ、変更を受諾ないし拒否してください Selected_entries=選択した項目 -Server_hostname=サーバーホスト名 -Server_type=サーバー型 Set_field=フィールドを設定 Set_fields=フィールドを設定 @@ -1294,7 +1291,6 @@ source_edit=ソースの編集 Special_name_formatters=名前の整形の定義 Special_table_columns=特殊な表列 -SQL_connection_established.=SQL接続が確立しました。 Starting_import=読み込みを開始 @@ -1528,8 +1524,6 @@ Fetching_Medline_by_term...=Medlineからterm順で取得中... %0_import_canceled=%0からの読み込みを取り消しました Please_enter_a_valid_number=有効な数値を入力してください Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=コンマ区切りでMedline_ID_(番号)ないしは検索項目の一覧を入力してください。 -Connect_to_external_SQL_database=外部SQLデータベースに接続 -Export_to_external_SQL_database=外部SQLデータベースに書き出す Show_search_results_in_a_window=検索結果をウィンドウに表示 Move_file_to_file_directory?=ファイルをファイルディレクトリに移動しますか? @@ -1544,7 +1538,6 @@ Unable_to_save_database=データベースを保存することができませ BibTeX_key_generator=BibTeX鍵の生成 Unable_to_open_link.=リンクを開くことができませんでした。 -Attempting_SQL_import...=SQLからの読み込みを試みています... Move_the_keyboard_focus_to_the_entry_table=キーボードのフォーカスを項目表に移動 MIME_type=MIME型 @@ -1786,21 +1779,10 @@ Doing_a_cleanup_for_%0_entries...=%0個の項目を消去しています... No_entry_needed_a_clean_up=消去の必要がある項目はありませんでした One_entry_needed_a_clean_up=1つの項目を消去する必要がありました %0_entries_needed_a_clean_up=%0個の項目を消去する必要がありました -Error_importing_from_database=データベースの読み込み時にエラー発生 Error_downloading_file_'%0'=ファイル「%0」をダウンロードする際にエラー発生 Download_failed=ダウンロードに失敗しました -%0_databases_will_be_imported=%0個のデータベースが読み込まれます -Importing_canceled=読み込みを取り消しました -There_are_no_available_databases_to_be_imported=読み込むデータベースがありません -Import_from_SQL_database=SQLデータベースから読み込み -Imported_%0_databases_successfully=%0個のデータベースが無事読み込まれました -<_CREATE_NEW_DATABASE_>=<新規データベースを作成> Remove_selected=選択分を削除 -SQL_Database_Exporter=SQLデータベース書き出し -Select_target_SQL_database\:=目的のSQLデータベースを選択してください\: -SQL_Database_Importer=SQLデータベース読み込み -Please_select_which_JabRef_databases_do_you_want_to_import\:=読み込みたいJabRefデータベースを選択してください\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.=グループツリーを解析できませんでした。BibTeXデータベースを保存すると、グループはすべて失われます。 Attach_file=ファイルを添付 @@ -2034,9 +2016,6 @@ Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modif Added_entry=項目を追加しました Added_new_'%0'_entry.=「%0」項目を新規に追加しました。 Choose_the_URL_to_download.=ダウンロードするURLを選んでください。 -Could_not_connect_to_SQL_database_for_the_following_reason\:=下記の理由により、SQLデータベースに接続できませんでした; -Could_not_export_to_SQL_database_for_the_following_reason\:=下記の理由により、SQLデータベースに書き出せませんでした; -Could_not_import_from_SQL_database_for_the_following_reason\:=下記の理由により、SQLデータベースから読み込めませんでした; Deleted_entry=項目を削除 Discard_changes=変更を放棄 Donate_to_JabRef=JabRefに寄付する @@ -2166,7 +2145,6 @@ wrong_entry_type_as_proceedings_has_page_numbers=項目型の誤り:proceeding Abbreviate_journal_names=学術誌名を短縮形化 Abbreviating...=短縮形化中 Adding_fetched_entries=取得した項目を追加しています -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?=既存のSQLデータベースを本当に削除しますか? Display_keywords_appearing_in_ALL_entries=全ての項目に現れるキーワードを表示 Display_keywords_appearing_in_ANY_entry=いずれかの項目に現れるキーワードを表示 Fetching_entries_from_Inspire=Inspireから項目を取得 @@ -2183,11 +2161,8 @@ Ill-formed_entrytype_comment_in_BIB_file=bibファイル中に誤った書式の Clipboard=クリップボード Could_not_paste_entry_as_text\:=項目をテキストとして貼り付けることができませんでした\: Do_you_still_want_to_continue?=それでも続けますか? -Please_enter_the_desired_name\:=望む名前を入力してください\: -SQL_Export=SQL書き出し This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:=このアクションは、次の各フィールドをそれぞれ少なくとも1項目以上書き換えます\: This_could_cause_undesired_changes_to_your_entries.=これはあなたの項目に望ましくない変更を加えるおそれがあります。 -You_have_entered_an_invalid_or_already_existent_DB_name.=無効あるいは既存のDB名を入力しました。 Run_field_formatter\:=フィールド整形子を起動します\: Table_font_size_is_%0=表フォント寸法は%0です Move_linked_files_to_default_file_directory_%0=リンクを張られたファイルを既定ファイルディレクトリ%0に移動します @@ -2401,3 +2376,35 @@ Entry_from_%0=%0からの項目 Merge_entry_with_%0_information=項目を%0情報とマージ Updated_entry_with_info_from_%0=%0からの情報で項目を更新しました +Connection= +Host= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_nl.properties b/src/main/resources/l10n/JabRef_nl.properties index 418d815f8f02..3733aa73e91d 100644 --- a/src/main/resources/l10n/JabRef_nl.properties +++ b/src/main/resources/l10n/JabRef_nl.properties @@ -121,7 +121,6 @@ Assigned_1_entry_to_group_"%0".=1_entry_aan_groep_"%0"_toegekend Attach_URL=URL_bijvoegen Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Poging_om_bestand_snelkoppelingen_voor_jouw_entries_automatisch_in_te_stellen._Automatisch_instellen_werkt_als_een_bestand_in_jouw_bestand_map_of_een_submap
een_identieke_naam_heeft_als_een_BibTeX-sleutel_van_een_entry,_plus_extensie. -Attempting_SQL_export...= Auto=Auto @@ -260,7 +259,6 @@ Column_width=Kolombreedte Command_line_id=Commandoregel_id Connect=Verbinden -Connect_to_SQL_database=Verbind_met_een_SQL_database Contained_in=bevat_in @@ -326,12 +324,13 @@ cut_entries=entries_knippen cut_entry=entry_knippen -Database= Database_encoding=Database_encodering Database_properties=Database_eigenschappen +Database_type= + Date_format=Datum_formaat Default=Standaard @@ -505,7 +504,6 @@ Error_while_writing=Foutmelding_bij_het_schrijven Error_writing_to_%0_file(s).= -Establishing_SQL_connection...= Exceptions=Uitzonderingen @@ -527,7 +525,6 @@ Export_preferences_to_file=Instellingen_exporteren_naar_bestand Export_properties=Eigenschappen_exporteren Export_to_clipboard=Exporteer_naar_klembord -Export_to_SQL_database= Exporting=Exporteren... Extension= @@ -962,6 +959,8 @@ Open_file=Open_bestand Open_last_edited_databases_at_startup=Open_laatst_aangepaste_databases_bij_het_opstarten +Open_shared_database= + Open_right-click_menu_with_Ctrl+left_button=Open_rechtsklik-menu_met_Ctrl_+_linkse_muisknop Open_terminal_here= @@ -1032,7 +1031,6 @@ Please_enter_the_string's_label=Geef_a.u.b._het_label_van_de_constante_in Please_select_an_importer.=Selecteer_a.u.b._een_importer. Please_select_exactly_one_group_to_move.=Selecteer_a.u.b._exact_\u00e9\u00e9n_groep_om_te_verplaatsen. -Please_specify_the= Possible_duplicate_entries=Mogelijke_dubbele_entries @@ -1055,6 +1053,7 @@ Problem_with_parsing_entry=Probleem_met_entry_ontleding Processing_%0= Program_output=Programma_uitvoer +Pull_changes_from_shared_database= Pushed_citations_to_%0= @@ -1236,8 +1235,6 @@ Select_new_ImportFormat_subclass=Selecteer_nieuw_ImportFormat_Subklasse Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Selecteer_de_boom_knopen_om_veranderingen_te_tonen_en_te_accepteren_of_afwijzen Selected_entries=Geselecteerde_entries -Server_hostname= -Server_type= Set_field=Veld_instellen Set_fields=Velden_instellen @@ -1313,7 +1310,6 @@ source_edit=broncode_aanpassen Special_name_formatters= Special_table_columns=Speciale_tabelkolommen -SQL_connection_established.= @@ -1544,8 +1540,6 @@ Fetching_Medline_by_term...= %0_import_canceled= Please_enter_a_valid_number= Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.= -Connect_to_external_SQL_database= -Export_to_external_SQL_database= Show_search_results_in_a_window= Move_file_to_file_directory?= @@ -1560,7 +1554,6 @@ Database_protection= Unable_to_save_database= BibTeX_key_generator= Unable_to_open_link.= -Attempting_SQL_import...= Move_the_keyboard_focus_to_the_entry_table= MIME_type= This_feature_lets_new_files_be_opened_or_imported_into_an_already_running_instance_of_JabRef
instead_of_opening_a_new_instance._For_instance,_this_is_useful_when_you_open_a_file_in_JabRef
from_your_web_browser.
Note_that_this_will_prevent_you_from_running_more_than_one_instance_of_JabRef_at_a_time.= @@ -1789,22 +1782,11 @@ Doing_a_cleanup_for_%0_entries...= No_entry_needed_a_clean_up= One_entry_needed_a_clean_up= %0_entries_needed_a_clean_up= -Error_importing_from_database= Error_downloading_file_'%0'= Download_failed= -%0_databases_will_be_imported= -Importing_canceled= -There_are_no_available_databases_to_be_imported= -Import_from_SQL_database= -Imported_%0_databases_successfully= -<_CREATE_NEW_DATABASE_>= Remove_selected= -SQL_Database_Exporter= -Select_target_SQL_database\:= -SQL_Database_Importer= -Please_select_which_JabRef_databases_do_you_want_to_import\:= Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.= Attach_file= @@ -2036,9 +2018,6 @@ Could_not_connect_to_%0= Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modified_groups_tree.= Added_entry= Added_new_'%0'_entry.= -Could_not_connect_to_SQL_database_for_the_following_reason\:= -Could_not_export_to_SQL_database_for_the_following_reason\:= -Could_not_import_from_SQL_database_for_the_following_reason\:= Deleted_entry= Discard_changes= Donate_to_JabRef= @@ -2190,7 +2169,6 @@ Run_field_formatter\:= Abbreviate_journal_names= Abbreviating...= Adding_fetched_entries= -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?= Display_keywords_appearing_in_ALL_entries= Display_keywords_appearing_in_ANY_entry= Fetching_entries_from_Inspire= @@ -2210,11 +2188,8 @@ Ill-formed_entrytype_comment_in_BIB_file= Clipboard= Could_not_paste_entry_as_text\:= Do_you_still_want_to_continue?= -Please_enter_the_desired_name\:= -SQL_Export= This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:= This_could_cause_undesired_changes_to_your_entries.= -You_have_entered_an_invalid_or_already_existent_DB_name.= Disable_highlight_groups_matching_entries= @@ -2435,3 +2410,35 @@ Entry_from_%0= Merge_entry_with_%0_information= Updated_entry_with_info_from_%0= +Connection= +Host= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_no.properties b/src/main/resources/l10n/JabRef_no.properties index de80c7fdb3a1..f388572cbdc7 100644 --- a/src/main/resources/l10n/JabRef_no.properties +++ b/src/main/resources/l10n/JabRef_no.properties @@ -132,7 +132,6 @@ Attach_URL=Tilordne_URL Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Pr\u00f8v_\u00e5_sette_fil-linker_automatisk_for_dine_enheter._Dette_virker_dersom_en_fil_i_fil-katalogen_din_eller_en_underkatalog
har_navn_likt_en_enhets_BibTeX-n\u00f8kkel,_pluss_etternavn. -Attempting_SQL_export...=Pr\u00f8ver_SQL-eksport... Auto=Auto @@ -275,7 +274,6 @@ Command_line_id=Kommandolinje-id Connect=Koble_til -Connect_to_SQL_database=Koble_til_SQL-database Contained_in=Inneholdt_i @@ -349,12 +347,13 @@ cut_entries=klippet_ut cut_entry=klipp_ut_enhet -Database=Database Database_encoding=Tegnkoding_for_database Database_properties=Databaseegenskaper +Database_type= + Date_format=Datoformat Default=Tilbakestill @@ -542,7 +541,6 @@ Error_while_writing=En_feil_oppsto_ved_skriving Error_writing_to_%0_file(s).=Feil_ved_skriving_til_%0_fil(er). -Establishing_SQL_connection...=Etablerer_SQL-forbindelse... Exceptions=Feilinformasjon @@ -566,7 +564,6 @@ Export_properties=Egenskaper_for_eksportfilter Export_to_clipboard=Eksporter_til_utklippstavle -Export_to_SQL_database=Eksporterer_til_SQL-database Exporting=Eksporterer @@ -1033,6 +1030,8 @@ Open_file=\u00c5pne_fil Open_last_edited_databases_at_startup=\u00c5pne_sist_viste_databaser_ved_oppstart +Open_shared_database= + Open_right-click_menu_with_Ctrl+left_button=\u00c5pne_h\u00f8yreklikkmeny_med_Ctrl+venstre_knapp Open_terminal_here= @@ -1111,7 +1110,6 @@ Please_select_an_importer.=Velg_et_importfilter. Please_select_exactly_one_group_to_move.=Velg_eksakt_en_gruppe_for_flytting. -Please_specify_the=Vennligst_spesifiser Possible_duplicate_entries=Mulige_duplikater @@ -1136,6 +1134,7 @@ Problem_with_parsing_entry=Problem_med_\u00e5_lese_enhet Processing_%0=Arbeider_%0 Program_output=Output_fra_program +Pull_changes_from_shared_database= Pushed_citations_to_%0=Sendte_enheter_til_%0 @@ -1327,9 +1326,7 @@ Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Velg_trenodene_for_\u Selected_entries=Valgte_enheter -Server_hostname=Tjenernavn -Server_type=Tjenertype Set_field=Sett_felt @@ -1413,7 +1410,6 @@ Special_name_formatters=Spesielle_navneformaterere Special_table_columns=Spesielle_kolonner -SQL_connection_established.=Etablerte_SQL-forbindelse @@ -1705,9 +1701,7 @@ Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=Ve -Connect_to_external_SQL_database=Koble_til_ekstern_SQL-database -Export_to_external_SQL_database=Eksporter_til_ekstern_SQL-database @@ -1736,7 +1730,6 @@ BibTeX_key_generator=BibTeX-n\u00f8kkelgenerator Unable_to_open_link.=Kan_ikke_\u00e5pne_link. -Attempting_SQL_import...=Pr\u00f8ver_SQL-import... Move_the_keyboard_focus_to_the_entry_table=Flytt_fokus_til_hovedtabellen @@ -2184,22 +2177,11 @@ No_entry_needed_a_clean_up= One_entry_needed_a_clean_up= %0_entries_needed_a_clean_up= -Error_importing_from_database=Feil_ved_import_fra_database Error_downloading_file_'%0'=Feil_ved_nedlasting_av_filen_'%0' Download_failed=Nedlasting_mislyktes -%0_databases_will_be_imported=%0_databaser_vil_bli_importert -Importing_canceled=Import_avbrutt -There_are_no_available_databases_to_be_imported=Det_er_ingen_databaser_tilgjengelig_for_import -Import_from_SQL_database=Import_fra_SQL-database -Imported_%0_databases_successfully=Importerte_%0_databaser -<_CREATE_NEW_DATABASE_>=<_OPPRETT_NY_DATABASE_> Remove_selected=Fjern_merkede -SQL_Database_Exporter=Eksporter_SQL-database -Select_target_SQL_database\:=Velg_m\u00e5ldatabase\: -SQL_Database_Importer=Importer_SQL-database -Please_select_which_JabRef_databases_do_you_want_to_import\:=Vennligst_velg_hvilke_JabRef-databaser_du_vil_importere\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.= Attach_file= @@ -2338,7 +2320,6 @@ Hostname= Invalid_setting= Network= Please_specify_both_hostname_and_port= -Port= Use_custom_proxy_configuration= Clear_connection_settings= Cleared_connection_settings.= @@ -2432,9 +2413,6 @@ Could_not_connect_to_%0=Kunne_ikke_koble_til_%0 Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modified_groups_tree.= Added_entry= Added_new_'%0'_entry.= -Could_not_connect_to_SQL_database_for_the_following_reason\:= -Could_not_export_to_SQL_database_for_the_following_reason\:= -Could_not_import_from_SQL_database_for_the_following_reason\:= Deleted_entry= Discard_changes= Donate_to_JabRef= @@ -2586,7 +2564,6 @@ Run_field_formatter\:= Abbreviate_journal_names= Abbreviating...= Adding_fetched_entries= -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?= Display_keywords_appearing_in_ALL_entries= Display_keywords_appearing_in_ANY_entry= Fetching_entries_from_Inspire= @@ -2606,11 +2583,8 @@ Ill-formed_entrytype_comment_in_BIB_file= Clipboard= Could_not_paste_entry_as_text\:= Do_you_still_want_to_continue?= -Please_enter_the_desired_name\:= -SQL_Export= This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:= This_could_cause_undesired_changes_to_your_entries.= -You_have_entered_an_invalid_or_already_existent_DB_name.= Disable_highlight_groups_matching_entries= @@ -2826,3 +2800,36 @@ Entry_from_%0= Merge_entry_with_%0_information= Updated_entry_with_info_from_%0= +Connection= +Host= +Port= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_pt_BR.properties b/src/main/resources/l10n/JabRef_pt_BR.properties index f0f23c4954f3..e377cddb7a48 100644 --- a/src/main/resources/l10n/JabRef_pt_BR.properties +++ b/src/main/resources/l10n/JabRef_pt_BR.properties @@ -64,7 +64,6 @@ Assigned_%0_entries_to_group_"%1".=%0_referências_atribuídas_ao_grupo_"%1" Assigned_1_entry_to_group_"%0".=1_referência_atribuído_ao_grupo_"%0" Attach_URL=Anexar_a_URL Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Tentativa_de_definir_automaticamente_links_de_arquivos_para_suas_referências._A_definição_automática_funciona_se_um_arquivo_em_seu_diretório_arquivo_ou_um_subdiretório
tem_o_mesmo_nome_de_uma_chave_BibTeX_de_uma_referência,_mais_sua_extensão. -Attempting_SQL_export...=Tentando_de_exportar_SQL Auto=Auto Autodetect_format=Detecção_automática_de_formato Autogenerate_BibTeX_keys=Geração_automática_de_chaves_BibTeX @@ -130,7 +129,6 @@ Color_for_marking_incomplete_entries=Cores_para_marcar_referências_incompletas Column_width=Largura_da_coluna Command_line_id=ID_da_linha_de_comando Connect=Conectar -Connect_to_SQL_database=Conectar_ao_banco_de_dados_SQL Contained_in=Contido_em Content=Conteúdo Copied=Copiado @@ -166,9 +164,10 @@ Customize_key_bindings=Personalizar_combinações_de_teclas Cut=Recortar cut_entries=Recortar_referências cut_entry=Recortar_referência -Database=Base_de_dados Database_encoding=Codificação_da_base_de_dados Database_properties=Propriedades_da_base_de_dados + +Database_type= Date_format=Formato_de_data Default=Padrão Default_encoding=Codificação_padrão @@ -257,7 +256,6 @@ Error_setting_field=Erro_ao_confgurar_campo Error_while_downloading_file\:=Erro_ao_realizar_o_download_do_arquivo_\: Error_while_writing=Erro_durante_a_escrita Error_writing_to_%0_file(s).=Erro_ao_escrever_para_%0_arquivos. -Establishing_SQL_connection...=Estabelecendo_conexão_SQL... Exceptions=Exceções Existing_file=Arquivo_existente '%0'_exists._Overwrite_file?='%0'_existe._Sobrescrever_o_arquivo? @@ -269,7 +267,6 @@ Export_preferences=Exportar_preferências Export_preferences_to_file=Exportar_preferências_do_arquivo Export_properties=Propriedades_de_exportação Export_to_clipboard=Exportar_para_a_área_de_transferência -Export_to_SQL_database=Exportar_para_um_Banco_de_Dados_SQL Exporting=Exportando Extension=Extensão External_changes=Modificações_externas @@ -488,6 +485,7 @@ Open_database=Abrir_base_de_dados Open_editor_when_a_new_entry_is_created=Abrir_o_editor_quando_uma_nova_referência_é_criada Open_file=Abrir_arquivo Open_last_edited_databases_at_startup=Abrir_as_últimas_base_de_dados_editadas_ao_iniciar +Open_shared_database= Open_right-click_menu_with_Ctrl+left_button=Abrir_menu_de_contexto_com_Ctrl+Botão_esquerdo_do_mouse Open_terminal_here= Open_URL_or_DOI=Abrir_URL_ou_DOI @@ -526,7 +524,6 @@ Please_enter_the_field_to_search_(e.g._keywords)_and_the_keyword_to_searc Please_enter_the_string's_label=Por_favor,_digite_o_rótulo_da_string Please_select_an_importer.=Por_favor,_selecione_um_importador. Please_select_exactly_one_group_to_move.=Por_favor,_selecione_exatamente_um_grupo_a_ser_movido. -Please_specify_the=Por_favor,_especifique_o Possible_duplicate_entries=Possíveis_referências_duplicadas Possible_duplicate_of_existing_entry._Click_to_resolve.=Possível_duplicata_de_referência_existente._Clique_para_resolver. Preamble=Preâmbulo @@ -539,6 +536,7 @@ Problem_with_parsing_entry=Problema_ao_analisar_a_referência Processing_%0=Processando_%0 Program_output=Saída_do_programa +Pull_changes_from_shared_database= Pushed_citations_to_%0=Citações_enviadas_para_%0 Quit_JabRef=Sair_do_JabRef Quit_synchronization=Sair_da_sincronização @@ -621,8 +619,6 @@ Select_file_from_ZIP-archive=Selecionar_arquivo_a_partir_de_um_arquivo_ZIP Select_new_ImportFormat_subclass=Selecionar_nova_subclasse_ImportFormat Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Selecione_os_nós_da_árvore_para_visualizar_e_aceitar_ou_rejeitar_mudanças Selected_entries=Referências_selecionadas -Server_hostname=Hostname_do_servidor -Server_type=Tipo_de_servidor Set_field=Configurar_campo Set_fields=Configurar_campos Set_general_fields=Definir_campos_gerais @@ -660,7 +656,6 @@ Sorted_immediate_subgroups.=Grupos_imediatos_ordenados. source_edit=edição_de_fonte Special_name_formatters=Formatadores_de_nome_espepciais Special_table_columns=Colunas_de_tabela_especiais -SQL_connection_established.=Conexão_SQL_estabelecida. Starting_import=Iniciando_importação Statically_group_entries_by_manual_assignment=Agrupar_referências_manualmente Status=Status @@ -790,8 +785,6 @@ Fetching_Medline_by_term...=Recuperando_do_Medline_por_termo... %0_import_canceled=Importação_a_partir_do_%0_cancelada Please_enter_a_valid_number=Por_favor,_digite_um_número_válido Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=Por_favor,_digite_uma_lista_separada_por_vírgulas_de_IDs_ou_termos_de_busca_Medline. -Connect_to_external_SQL_database=Conectar_a_um_banco_de_dados_SQL_externo -Export_to_external_SQL_database=Exportar_para_um_banco_de_dados_SQL_externo Show_search_results_in_a_window=Exibir_resultados_de_busca_em_uma_janela Move_file_to_file_directory?=Mover_arquivo_para_o_diretório_de_arquivos? Rename_to_'%0'=Renomear_para_'%0' @@ -803,7 +796,6 @@ Database_protection=Proteção_da_base_de_dados Unable_to_save_database=Não_foi_possível_salvar_a_base_de_dados BibTeX_key_generator=Gerador_de_chaves_BibTeX Unable_to_open_link.=Não_foi_possível_abrir_link. -Attempting_SQL_import...=Tentativa_de_importação_SQL... Move_the_keyboard_focus_to_the_entry_table=Mover_o_foco_do_teclado_para_a_tabela_de_referências MIME_type=MIME_type This_feature_lets_new_files_be_opened_or_imported_into_an_already_running_instance_of_JabRef
instead_of_opening_a_new_instance._For_instance,_this_is_useful_when_you_open_a_file_in_JabRef
from_your_web_browser.
Note_that_this_will_prevent_you_from_running_more_than_one_instance_of_JabRef_at_a_time.=Esta_funcionalidade_permite_que_novos_arquivos_sejam_abertos_ou_importados_para_uma_instância_do_JabRef_já_aberta
_ao_invés_de_abrir_uma_nova_instância._Por_exemplo,_isto_é_útil_quando_você_abre_um_arquivo_no_JabRef
_a_partir_de_ser_navegador_web.
_Note_que_isto_irá_previnir_que_você_execute_uma_ou_mais_instâncias_do_JabRef_ao_mesmo_tempo. @@ -1018,21 +1010,10 @@ Doing_a_cleanup_for_%0_entries...=Limpando_%0_entradas... No_entry_needed_a_clean_up=Nenhuma_referência_precisou_de_limpeza One_entry_needed_a_clean_up=Uma_referência_necessitou_limpeza %0_entries_needed_a_clean_up=%0_entradas_necessitaram_limpeza -Error_importing_from_database=Erro_ao_importar_do_banco_de_dados Error_downloading_file_'%0'=Erro_ao_baixar_arquivo_'%0' Download_failed=O_download_falhou -%0_databases_will_be_imported=%0_bases_de_dados_serão_importados -Importing_canceled=Importação_cancelada -There_are_no_available_databases_to_be_imported=Não_existem_bases_de_dados_disponíveis_para_importação -Import_from_SQL_database=Importar_de_um_banco_de_dados_SQL -Imported_%0_databases_successfully=%0_bases_de_dados_foram_importadas_com_sucesso -<_CREATE_NEW_DATABASE_>=<_CRIAR_NOVA_BASE_DE_DADOS_> Remove_selected=Remover_Selecionados -SQL_Database_Exporter=Exportador_de_Bases_de_Dados_SQL -Select_target_SQL_database\:=Selecione_a_base_de_dados_SQL_de_destino\: -SQL_Database_Importer=Importador_de_Bases_de_Dados_SQL -Please_select_which_JabRef_databases_do_you_want_to_import\:=Por_favor_selecione_qual_base_de_dados_deseja_importar_ao_JabRef\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.=Árvore_de_agrupamento_não_pode_ser_interpretada._Se_você_salvar_sua_base_de_dados_BibTeX,_todos_os_grupos_serão_perdidos Attach_file=Anexar_arquivo @@ -1171,7 +1152,6 @@ Hostname=Host Invalid_setting=Configuração_Inválida Network=Rede Please_specify_both_hostname_and_port=Por_favor,_especifique_o_hostname_e_a_porta -Port=Porta Use_custom_proxy_configuration=Usar_configurações_personalizadas_de_proxy Clear_connection_settings=Limpar_configurações_da_conexão Cleared_connection_settings.=Configurações_de_conexão_foram_limpas. @@ -1263,9 +1243,6 @@ Could_not_connect_to_%0=Não_foi_possível_conectar_a_%0 Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modified_groups_tree.= Added_entry=Referência_adicionada Added_new_'%0'_entry.= -Could_not_connect_to_SQL_database_for_the_following_reason\:= -Could_not_export_to_SQL_database_for_the_following_reason\:= -Could_not_import_from_SQL_database_for_the_following_reason\:= Deleted_entry=Referência_deletada Discard_changes= Donate_to_JabRef= @@ -1412,7 +1389,6 @@ wrong_entry_type_as_proceedings_has_page_numbers= Abbreviate_journal_names=Abreviar_nomes_de_periódicos Abbreviating...=Abreviando... Adding_fetched_entries=Adicionando_referências_recuperadas -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?= Display_keywords_appearing_in_ALL_entries= Display_keywords_appearing_in_ANY_entry= Fetching_entries_from_Inspire= @@ -1428,11 +1404,8 @@ Ill-formed_entrytype_comment_in_BIB_file= Clipboard=Área_de_transferência Could_not_paste_entry_as_text\:= Do_you_still_want_to_continue?= -Please_enter_the_desired_name\:= -SQL_Export=Exportar_SQL This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:= This_could_cause_undesired_changes_to_your_entries.= -You_have_entered_an_invalid_or_already_existent_DB_name.= Disable_highlight_groups_matching_entries= Run_field_formatter\:= @@ -1656,3 +1629,36 @@ Entry_from_%0= Merge_entry_with_%0_information= Updated_entry_with_info_from_%0= +Connection= +Host= +Port= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_ru.properties b/src/main/resources/l10n/JabRef_ru.properties index 5169726d29c0..c3e5ce8f7972 100644 --- a/src/main/resources/l10n/JabRef_ru.properties +++ b/src/main/resources/l10n/JabRef_ru.properties @@ -107,7 +107,6 @@ Assigned_1_entry_to_group_"%0".=Назначена_1_запись_группе_" Attach_URL=Добавить_URL-адрес Attempt_to_automatically_set_file_links_for_your_entries._Automatically_setting_works_if_a_file_in_your_file_directory
or_a_subdirectory_is_named_identically_to_an_entry's_BibTeX_key,_plus_extension.=Попытка_автоопределения_ссылок_файл_для_записей._Автоопределение_возможно_если_файл_находящийся_в_каталоге_файлов_файл_или_его_подкаталоге
имеет_имя,_идентичное_имени_ключа_BibTeX_для_записи,_с_соответствующим_разрешением. -Attempting_SQL_export...=Попытка_экспорта_SQL... Auto=Авто @@ -242,7 +241,6 @@ Column_width=Ширина_столбца Command_line_id=Ид._командной_строки Connect=Подключение -Connect_to_SQL_database=Подключение_к_БД_SQL Contained_in=Содержится_в @@ -307,12 +305,13 @@ cut_entries=вырезать_записи cut_entry=вырезать_запись -Database=База_данных Database_encoding=Кодировка_БД Database_properties=Свойства_БД +Database_type= + Date_format=Формат_БД Default=По_умолчанию @@ -481,7 +480,6 @@ Error_while_writing=Ошибка_при_записи Error_writing_to_%0_file(s).=Ошибка_при_записи_в_файл(ы)_%0. -Establishing_SQL_connection...=Выполняется_подключение_SQL... Exceptions=Исключения Existing_file=Существующий_файл @@ -502,7 +500,6 @@ Export_preferences_to_file=Экспорт_пользовательских_на Export_properties=Экспорт_свойств Export_to_clipboard=Экспорт_в_буфер_обмена -Export_to_SQL_database=Экспорт_в_БД_SQL Exporting=Экспорт Extension=Расширение @@ -928,6 +925,8 @@ Open_file=Открыть_файл Open_last_edited_databases_at_startup=Открыть_БД_с_последним_временем_редактирования_при_запуске +Open_shared_database= + Open_right-click_menu_with_Ctrl+left_button=Открыть_контекстное_меню_сочетанием_Ctrl+левая_кнопка_мыши Open_terminal_here= @@ -1000,7 +999,6 @@ Please_enter_the_string's_label=Введите_подпись_для_строк Please_select_an_importer.=Выберите_фильтр_импорта. Please_select_exactly_one_group_to_move.=Выберите_только_одну_группу_для_перемещения. -Please_specify_the=Укажите Possible_duplicate_entries=Возможные_дубликаты_записей @@ -1022,6 +1020,7 @@ Problem_with_parsing_entry=Ошибка_анализа_записи Processing_%0=Выполняется_обработка_%0 Program_output=Вывод_программы +Pull_changes_from_shared_database= Pushed_citations_to_%0=Цитаты_переданы_в_%0 @@ -1193,8 +1192,6 @@ Select_new_ImportFormat_subclass=Выбрать_новый_подкласс_Impo Select_the_tree_nodes_to_view_and_accept_or_reject_changes=Выбрать_узлы_дерева_для_просмотра_и_применения/отклонения_изменений Selected_entries=Записи_выбраны -Server_hostname=Имя_хоста_сервера -Server_type=Тип_сервера Set_field=Настройка_поля Set_fields=Настройка_полей @@ -1270,7 +1267,6 @@ source_edit=изменение_источника Special_name_formatters=Особые_программы_форматирования_имени Special_table_columns=Особые_столбцы_таблицы -SQL_connection_established.=Выполнено_подключение_SQL. Starting_import=Запустить_импорт @@ -1502,8 +1498,6 @@ Fetching_Medline_by_id...=Выполняется_выборка_Medline_по_и Fetching_Medline_by_term...=Выполняется_выборка_Medline_по_условию... Please_enter_a_valid_number=Введите_допустимое_число Please_enter_a_comma_separated_list_of_Medline_IDs_(numbers)_or_search_terms.=Введите_список_ид._Medline_(числ.),_разделенных_запятыми_или_условия_поиска. -Connect_to_external_SQL_database=Подключение_к_внешней_БД_SQL -Export_to_external_SQL_database=Экспорт_во_внешнюю_БД_SQL Show_search_results_in_a_window=Показать_результаты_в_окне Move_file_to_file_directory?=Файл_будет_перемещен_в_каталог_файлов._Продолжить? @@ -1518,7 +1512,6 @@ Unable_to_save_database=Не_удалось_сохранить_БД BibTeX_key_generator=Генератор_ключей_BibTeX Unable_to_open_link.=Не_удалось_перейти_по_ссылке. -Attempting_SQL_import...=Попытка_импорта_SQL... Move_the_keyboard_focus_to_the_entry_table=Переместить_курсор_в_таблицу_записей MIME_type=MIME-тип @@ -1758,22 +1751,11 @@ Doing_a_cleanup_for_%0_entries...=Выполняется_очистка_для_% No_entry_needed_a_clean_up=Нет_записей_для_очистки One_entry_needed_a_clean_up=Необходима_очистка_для_одной_записи %0_entries_needed_a_clean_up=Необходима_очистка_для_%0_записей -Error_importing_from_database=Ошибка_импорта_из_БД Error_downloading_file_'%0'=Ошибка_загрузки_файла_'%0' Download_failed=Ошибка_загрузки -%0_databases_will_be_imported=Баз_данных_для_импорта\:_%0 -Importing_canceled=Импорт_отменен -There_are_no_available_databases_to_be_imported=Нет_доступных_БД_для_импорта -Import_from_SQL_database=Импорт_из_БД_SQL -Imported_%0_databases_successfully=Успешно_импортировано_%0_БД -<_CREATE_NEW_DATABASE_>=<_СОЗДАТЬ_НОВУЮ_БД_> Remove_selected=Удалить_выбранное -SQL_Database_Exporter=Средства_экспорта_БД_SQL -Select_target_SQL_database\:=Указать_целевую_БД_SQL\: -SQL_Database_Importer=Средства_импорта_БД_SQL -Please_select_which_JabRef_databases_do_you_want_to_import\:=Укажите_БД_JabRef_для_импорта\: Group_tree_could_not_be_parsed._If_you_save_the_BibTeX_database,_all_groups_will_be_lost.=Не_удалось_проанализировать_дерево_групп._При_сохранении_БД_BibTeX_все_группы_будут_утеряны. Attach_file=Приложить_файл @@ -2007,9 +1989,6 @@ Push_to_%0=Передать_в_%0 Accepting_the_change_replaces_the_complete_groups_tree_with_the_externally_modified_groups_tree.= Added_entry= Added_new_'%0'_entry.= -Could_not_connect_to_SQL_database_for_the_following_reason\:= -Could_not_export_to_SQL_database_for_the_following_reason\:= -Could_not_import_from_SQL_database_for_the_following_reason\:= Deleted_entry= Discard_changes= Donate_to_JabRef= @@ -2157,7 +2136,6 @@ wrong_entry_type_as_proceedings_has_page_numbers= Abbreviate_journal_names= Abbreviating...= Adding_fetched_entries= -Are_you_sure_you_want_to_remove_the_already_existent_SQL_DBs?= Display_keywords_appearing_in_ALL_entries= Display_keywords_appearing_in_ANY_entry= Fetching_entries_from_Inspire= @@ -2173,11 +2151,8 @@ Ill-formed_entrytype_comment_in_BIB_file= Clipboard= Could_not_paste_entry_as_text\:= Do_you_still_want_to_continue?= -Please_enter_the_desired_name\:= -SQL_Export= This_action_will_modify_the_following_field(s)_in_at_least_one_entry_each\:= This_could_cause_undesired_changes_to_your_entries.= -You_have_entered_an_invalid_or_already_existent_DB_name.= Disable_highlight_groups_matching_entries= Run_field_formatter\:= @@ -2402,3 +2377,35 @@ Entry_from_%0= Merge_entry_with_%0_information= Updated_entry_with_info_from_%0= +Connection= +Host= +Database= +User= +Connection_error= +Driver_error= +Connection_to_%0_server_stablished.= +Required_field_"%0"_is_empty.= +Corrupt_shared_database_structure.= +Integrity_check_failed._Fixing...= + +%0_driver_not_available.= + +The_connection_to_the_server_has_been_determinated.= + +Connection_lost.= +Reconnect= +Work_offline= + +Working_offline.= + +Update_refused.= +Update_refused= +Local_entry= +Shared_entry= +Update_could_not_be_performed_due_to_existing_change_conflicts.= +You_are_not_working_on_the_newest_version_of_BibEntry.= +Local_version\:_%0= +Shared_version\:_%0= +Please_merge_the_shared_entry_with_yours_and_press_"Merge_entries"_to_resolve_this_problem.= +Canceling_this_operation_will_leave_your_changes_unsynchronized._Cancel_anyway?= +The_BibEntry_you_currently_work_on_has_been_deleted_on_the_shared_side._Hit_"Keep"_to_recover_the_entry.= diff --git a/src/main/resources/l10n/JabRef_sv.properties b/src/main/resources/l10n/JabRef_sv.properties index 4ca0860f14e9..eb6b73a4d045 100644 --- a/src/main/resources/l10n/JabRef_sv.properties +++ b/src/main/resources/l10n/JabRef_sv.properties @@ -1,7 +1,6 @@ # Swedish translation of JabRef %0_contains_the_regular_expression_%1=%0_innehåller_det_reguljära_uttrycket_%1 %0_contains_the_term_%1=%0_innehåller_termen_%1 -%0_databases_will_be_imported=%0_databaser_kommer_att_importeras %0_doesn't_contain_the_regular_expression_%1=%0_innehåller_inte_det_reguljära_uttrycket_%1 %0_doesn't_contain_the_term_%1=%0_innehåller_inte_termen_%1 %0_entries_found._To_reduce_server_load,_only_%1_will_be_downloaded.=%0_poster_hittades._För_att_minska_lasten_på_servern_kommer_bara_%1_att_laddas_ned. @@ -16,7 +15,6 @@ '%0'_is_not_a_valid_ADS_bibcode.='%0'_är_inte_en_giltig_ADS-bibkod. Could_not_find_file_'%0'
linked_from_entry_'%1'=Hittar_inte_filen_'%0'
som_länkas_från_posten_'%1' = -<_CREATE_NEW_DATABASE_>=<_SKAPA_NY_DATABAS_> All_Entries_(this_group_cannot_be_edited_or_removed)=Alla_poster_(den_här_gruppen_kan_inte_ändras_eller_tas_bort) =