Project

General

Profile

Download (10.3 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2009 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.io.dwca.in;
10

    
11
import java.sql.ResultSet;
12
import java.sql.SQLException;
13
import java.util.HashMap;
14
import java.util.HashSet;
15
import java.util.Map;
16
import java.util.Map.Entry;
17
import java.util.Set;
18

    
19
import org.apache.log4j.Logger;
20

    
21
import eu.etaxonomy.cdm.database.CdmDataSource;
22
import eu.etaxonomy.cdm.database.CdmPersistentDataSource;
23
import eu.etaxonomy.cdm.database.DataSourceNotFoundException;
24
import eu.etaxonomy.cdm.database.ICdmDataSource;
25
import eu.etaxonomy.cdm.model.agent.Person;
26
import eu.etaxonomy.cdm.model.agent.Team;
27
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
28
import eu.etaxonomy.cdm.model.name.TaxonName;
29
import eu.etaxonomy.cdm.model.reference.Reference;
30
import eu.etaxonomy.cdm.model.taxon.Synonym;
31
import eu.etaxonomy.cdm.model.taxon.Taxon;
32

    
33
/**
34
 * TODO inwork, currently it works with H2
35
 * @author a.mueller
36
 * @created 22.03.2012
37
 *
38
 */
39
public class DatabaseMapping implements IImportMapping {
40
    private static final Logger logger = Logger.getLogger(DatabaseMapping.class);
41

    
42
	private static final String DATABASE_INTERNAL_IMPORT_MAPPING = "_internalImportMapping";
43
	protected static final String TABLE_IMPORT_MAPPING  = "importmapping";
44

    
45
	private static final String COL_TASK_ID = "task_id";
46

    
47
	private static final String COL_SOURCE_NS = "source_namespace";
48

    
49
	private static final String COL_SOURCE_ID = "source_id";
50

    
51
	private static final String COL_DEST_NS = "destination_namespace";
52

    
53
	private static final String COL_DEST_ID = "destination_id";
54

    
55
    private static final int SOURCE_KEY_LENGTH = 255;
56

    
57

    
58
	private ICdmDataSource datasource;
59
	private final String mappingId;
60
	private final Map<String, Class> shortCuts = new HashMap<>();
61
	private final Map<Class, String> reverseShortCuts = new HashMap<>();
62

    
63

    
64
	@Override
65
	public void putMapping(String namespace, Integer sourceKey, IdentifiableEntity destinationObject){
66
		putMapping(namespace, String.valueOf(sourceKey), destinationObject);
67
	}
68

    
69
    public DatabaseMapping(String mappingId) {
70
        this(mappingId, null);
71
    }
72

    
73
	/**
74
	 * @param database
75
	 */
76
	public DatabaseMapping(String mappingId, String file) {
77
		super();
78
		initDatasource(file);
79
		this.mappingId = mappingId;
80
	}
81

    
82
	@Override
83
	public void putMapping(String namespace, String sourceKey, IdentifiableEntity destinationObject){
84
		CdmKey<IdentifiableEntity<?>> cdmKey = new CdmKey<>(destinationObject);
85
		putMapping(namespace, sourceKey, cdmKey);
86
	}
87

    
88
	public void putMapping(String namespace, String sourceKey, CdmKey<IdentifiableEntity<?>> cdmKey) {
89
		try {
90
		    String normalizedKey = normalizeKey(sourceKey);
91

    
92
			deleteExistingMapping(namespace, sourceKey);
93
			persistNotExistingMapping(namespace, normalizedKey, cdmKey);
94
		} catch (SQLException e) {
95
			throw new RuntimeException(e);
96
		}
97
	}
98

    
99
	private int persistNotExistingMapping(String sourceNamespace, String normalizedKey, CdmKey<IdentifiableEntity<?>> cdmKey) throws SQLException {
100

    
101
		//cdm namespace
102
		String clazz = getCdmClassStr(cdmKey.clazz);
103

    
104
		//insert
105
		String insertMappingSql = " INSERT INTO %s (%s, %s, %s, %s, %s)" +
106
			" VALUES ('%s','%s','%s','%s','%s')";
107
		insertMappingSql = String.format(insertMappingSql,
108
				TABLE_IMPORT_MAPPING, COL_TASK_ID, COL_SOURCE_NS, COL_SOURCE_ID, COL_DEST_NS, COL_DEST_ID,
109
				this.mappingId, sourceNamespace, normalizedKey, clazz, cdmKey.id);
110
		return this.datasource.executeUpdate(insertMappingSql);
111
	}
112

    
113

    
114
	/**
115
	 * @param cdmKey
116
	 * @return
117
	 */
118
	private String getCdmClassStr(Class cdmClass) {
119
		String clazz = reverseShortCuts.get(cdmClass);
120
		if (clazz == null){
121
			clazz = cdmClass.getCanonicalName();
122
		}
123
		return clazz;
124
	}
125

    
126

    
127
	private int deleteExistingMapping(String sourceNamespace, String sourceId) throws SQLException {
128
	    String normalizedKey = normalizeKey(sourceId);
129
		String deleteMappingSql = " DELETE FROM %s WHERE %s = '%s' AND %s = '%s' AND %s = '%s'";
130
		deleteMappingSql = String.format(deleteMappingSql,TABLE_IMPORT_MAPPING, COL_TASK_ID, this.mappingId, COL_SOURCE_NS, sourceNamespace, COL_SOURCE_ID, normalizedKey);
131
		return this.datasource.executeUpdate(deleteMappingSql);
132
	}
133

    
134

    
135
	private int deleteAll() throws SQLException {
136
		String deleteMappingSql = " DELETE FROM %s WHERE %s = '%s' ";
137
		deleteMappingSql = String.format(deleteMappingSql,TABLE_IMPORT_MAPPING, COL_TASK_ID, this.mappingId);
138
		return this.datasource.executeUpdate(deleteMappingSql);
139
	}
140

    
141
	public int size() throws SQLException {
142
		String sql = " SELECT count(*) as n FROM %s WHERE %s = '%s' ";
143
		sql = String.format(sql,TABLE_IMPORT_MAPPING, COL_TASK_ID, this.mappingId);
144
		ResultSet rs = this.datasource.executeQuery(sql);
145
		rs.next();
146
		return rs.getInt("n");
147

    
148
	}
149

    
150
	@Override
151
	public Set<CdmKey> get(String sourceNamespace, String sourceId) {
152
		Set<CdmKey> result = new HashSet<>();
153
		String normalizedKey = normalizeKey(sourceId);
154
		String selectMappingSql = " SELECT %s, %s FROM %s" +
155
				" WHERE %s = '%s' AND %s = '%s' AND %s = '%s' ";
156
		selectMappingSql = String.format(selectMappingSql,
157
				COL_DEST_NS, COL_DEST_ID, TABLE_IMPORT_MAPPING,
158
				COL_TASK_ID, this.mappingId, COL_SOURCE_NS, sourceNamespace,
159
				COL_SOURCE_ID , normalizedKey);
160
		try {
161
			ResultSet rs = this.datasource.executeQuery(selectMappingSql);
162
			while (rs.next()){
163
				String clazzStr = rs.getString(COL_DEST_NS);
164
				Object id = rs.getObject(COL_DEST_ID);
165
				if (id == null){
166
					throw new RuntimeException("Destination id for import mapping is 'null'");
167
				}
168

    
169
				Class<?> clazz = getCdmClass(clazzStr);
170

    
171
				CdmKey<?> key = new CdmKey<>(clazz, Integer.valueOf(String.valueOf(id)));
172
				result.add(key);
173
			}
174
		} catch (NumberFormatException e) {
175
			throw new RuntimeException(e);
176
		} catch (SQLException e) {
177
			throw new RuntimeException(e);
178
		} catch (ClassNotFoundException e) {
179
			throw new RuntimeException(e);
180
		}
181

    
182
		return result;
183
	}
184

    
185
	@Override
186
    public boolean exists(String sourceNamespace, String sourceId, Class<?> destinationClass){
187
		String selectMappingSql = " SELECT count(*) as n FROM %s" +
188
			" WHERE %s = '%s' AND %s = '%s' AND %s = '%s' AND %s = '%s' ";
189

    
190

    
191
		String cdmClass = getCdmClassStr(destinationClass);
192
		String normalizedKey = normalizeKey(sourceId);
193
		selectMappingSql = String.format(selectMappingSql,
194
			TABLE_IMPORT_MAPPING, COL_TASK_ID, this.mappingId,
195
			COL_SOURCE_NS, sourceNamespace, COL_SOURCE_ID , normalizedKey, COL_DEST_NS, cdmClass);
196
		try {
197
			ResultSet rs = this.datasource.executeQuery(selectMappingSql);
198
			rs.next();
199
			int n = rs.getInt("n");
200

    
201
			return n > 0;
202
		} catch (SQLException e) {
203
			throw new RuntimeException(e);
204
		}
205
	}
206

    
207
    /**
208
     * Normalizes the key coming from the DwCA File.
209
     * This includes handling ' and keys with length > 255
210
     * @param sourceKey
211
     * @return
212
     */
213
    private String normalizeKey(String key) {
214
        if (key == null){
215
            return null;
216
        }
217
        String result = key.replace("'", "''");
218
        if (result.length() > SOURCE_KEY_LENGTH){
219
            //TODO better use MD5 hash or similar
220
            logger.info("Source key was trunkated: " + key);
221
            result = result.substring(0, SOURCE_KEY_LENGTH);
222
        }
223
        return result;
224
    }
225

    
226
	@Override
227
	public InMemoryMapping getPartialMapping( Map<String, Set<String>> namespacedSourceKeys) {
228
		InMemoryMapping partialMapping = new InMemoryMapping();
229
		for (Entry<String,Set<String>> entry  : namespacedSourceKeys.entrySet()){
230
			String namespace = entry.getKey();
231
			for (String sourceKey : entry.getValue() ){
232
			    String normalizedKey = normalizeKey(sourceKey);
233
				Set<CdmKey> destObjects = this.get(namespace, normalizedKey);
234
				for (CdmKey cdmKey : destObjects){
235
					partialMapping.putMapping(namespace, normalizedKey, cdmKey);
236
				}
237
			}
238
		}
239
		return partialMapping;
240
	}
241

    
242

    
243
	@Override
244
	public void finish() {
245
		try {
246
			int count = size();
247
			deleteAll();
248
			logger.info("Finalize database mapping " +  count +  ": " + size());
249
		} catch (SQLException e) {
250
			throw new RuntimeException();
251
		}
252
	}
253

    
254

    
255
	/**
256
	 * @param clazzStr
257
	 * @return
258
	 * @throws ClassNotFoundException
259
	 */
260
	private Class<?> getCdmClass(String clazzStr) throws ClassNotFoundException {
261
		Class<?> clazz = shortCuts.get(clazzStr);
262
		if (clazz == null){
263
			clazz = Class.forName(clazzStr);
264
		}
265
		return clazz;
266
	}
267

    
268

    
269
	private void initDatasource(String file) {
270
		getDatabase(file);
271
		shortCuts.put("TaxonName", TaxonName.class);
272
		shortCuts.put("Taxon", Taxon.class);
273
		shortCuts.put("Synonym", Synonym.class);
274
		shortCuts.put("Reference", Reference.class);
275
		shortCuts.put("Team", Team.class);
276
		shortCuts.put("Person", Person.class);
277
		//reverse
278
		for (String key :shortCuts.keySet()){
279
			reverseShortCuts.put(shortCuts.get(key), key);
280
		}
281
	}
282

    
283
	public ICdmDataSource getDatabase(){
284
	    return getDatabase(null);
285
	}
286

    
287
	public ICdmDataSource getDatabase(String path){
288
		try {
289
			try {
290
			    if (path == null){
291
			        datasource = CdmPersistentDataSource.NewInstance(DATABASE_INTERNAL_IMPORT_MAPPING);
292
			    }else{
293
			        makeDatasource(path);
294
			    }
295
			} catch (DataSourceNotFoundException e) {
296
				makeDatasource(path);
297
				CdmPersistentDataSource.save(DATABASE_INTERNAL_IMPORT_MAPPING, datasource);
298
			}
299
			datasource.executeQuery("SELECT * FROM " + TABLE_IMPORT_MAPPING);
300
		} catch (SQLException e) {
301
			//create database structure
302
		    try {
303
				String strCreateTable = "CREATE TABLE IF NOT EXISTS %s (";
304
				strCreateTable += "%s nvarchar(36) NOT NULL,";
305
				strCreateTable += "%s nvarchar(100) NOT NULL,";
306
				strCreateTable += "%s nvarchar(" + SOURCE_KEY_LENGTH + ") NOT NULL,";
307
				strCreateTable += "%s nvarchar(100) NOT NULL,";
308
				strCreateTable += "destination_id nvarchar(50) NOT NULL,";
309
				strCreateTable += "PRIMARY KEY (task_id, source_namespace, source_id)";
310
				strCreateTable += ") ";
311
				strCreateTable = String.format(strCreateTable, TABLE_IMPORT_MAPPING, COL_TASK_ID, COL_SOURCE_NS, COL_SOURCE_ID, COL_DEST_NS, COL_DEST_ID);
312
				datasource.executeUpdate(strCreateTable);
313
				logger.warn("Mapping database structure created");
314
			} catch (SQLException e1) {
315
				e1.printStackTrace();
316
				throw new RuntimeException(e1);
317
			}
318
		}
319
		return datasource;
320
	}
321

    
322
    /**
323
     * @param path
324
     */
325
    private void makeDatasource(String path) {
326
        datasource = CdmDataSource.NewH2EmbeddedInstance("_tmpMapping", "a", "b", path);
327
    }
328

    
329

    
330
}
(3-3/37)