001: /*
002: * Copyright 2008 Outerthought bvba and Schaubroeck nv
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.outerj.daisy.repository.test;
017:
018: import org.outerj.daisy.repository.*;
019: import org.outerj.daisy.repository.schema.*;
020: import org.outerj.daisy.repository.variant.Language;
021: import org.outerj.daisy.repository.testsupport.AbstractDaisyTestCase;
022: import org.outerj.daisy.repository.user.Role;
023: import org.outerj.daisy.tools.importexport.export.tm.TMExporter;
024: import org.outerj.daisy.tools.importexport.export.fs.ExportFile;
025: import org.outerj.daisy.tools.importexport.export.fs.LocalExportFile;
026: import org.outerj.daisy.tools.importexport.export.config.ExportOptions;
027: import org.outerj.daisy.tools.importexport.export.config.ExportOptionsFactory;
028: import org.outerj.daisy.tools.importexport.export.BaseExportListener;
029: import org.outerj.daisy.tools.importexport.docset.DocumentSet;
030: import org.outerj.daisy.tools.importexport.docset.QueryDocumentSet;
031: import org.outerj.daisy.tools.importexport.tm.TMConfig;
032: import org.outerj.daisy.tools.importexport.import_.tm.TMImporter;
033: import org.outerj.daisy.tools.importexport.import_.fs.ImportFile;
034: import org.outerj.daisy.tools.importexport.import_.fs.local.LocalImportFile;
035: import org.outerj.daisy.tools.importexport.import_.config.ImportOptions;
036: import org.outerj.daisy.tools.importexport.import_.config.ImportOptionsFactory;
037: import org.outerj.daisy.tools.importexport.import_.BaseImportListener;
038: import org.outerj.daisy.tools.importexport.import_.documents.DocumentImportResult;
039: import org.outerj.daisy.tools.importexport.import_.schema.SchemaLoadListener;
040: import org.outerj.daisy.htmlcleaner.HtmlCleanerTemplate;
041: import org.outerj.daisy.htmlcleaner.HtmlCleanerFactory;
042: import org.outerj.daisy.xmlutil.SimpleNamespaceContext;
043: import org.outerj.daisy.xmlutil.LocalXPathFactory;
044: import org.outerj.daisy.xmlutil.LocalTransformerFactory;
045: import org.xml.sax.ContentHandler;
046: import org.xml.sax.SAXException;
047: import org.xml.sax.InputSource;
048: import org.w3c.dom.Element;
049: import org.w3c.dom.NodeList;
050:
051: import javax.xml.parsers.DocumentBuilderFactory;
052: import javax.xml.xpath.XPath;
053: import javax.xml.xpath.XPathConstants;
054: import javax.xml.transform.Transformer;
055: import javax.xml.transform.TransformerException;
056: import javax.xml.transform.stream.StreamResult;
057: import javax.xml.transform.dom.DOMSource;
058: import java.io.File;
059: import java.util.Date;
060:
061: /**
062: * Testcase for translation management export-import.
063: */
064: public abstract class AbstractTMImpExpTest extends
065: AbstractDaisyTestCase {
066: protected boolean resetDataStores() {
067: return true;
068: }
069:
070: public void testImpExp() throws Exception {
071: RepositoryManager repositoryManager = getRepositoryManager();
072: Repository repository = repositoryManager
073: .getRepository(new Credentials("testuser", "testuser"));
074: repository.switchRole(Role.ADMINISTRATOR);
075:
076: //
077: // Init repo state
078: //
079:
080: // Create languages
081: Language enLang = repository.getVariantManager()
082: .createLanguage("en");
083: enLang.save();
084:
085: Language nlLang = repository.getVariantManager()
086: .createLanguage("nl");
087: nlLang.save();
088:
089: // Create schema
090: RepositorySchema schema = repository.getRepositorySchema();
091:
092: PartType htmlContent = schema.createPartType("HtmlContent",
093: "text/xml");
094: htmlContent.setDaisyHtml(true);
095: htmlContent.save();
096:
097: PartType binaryContent = schema.createPartType("BinaryContent",
098: "");
099: binaryContent.save();
100:
101: FieldType stringField = schema.createFieldType("StringField",
102: ValueType.STRING);
103: stringField.save();
104:
105: FieldType mvStringField = schema.createFieldType(
106: "MVStringField", ValueType.STRING, true);
107: mvStringField.save();
108:
109: FieldType stringWithSelListField = schema.createFieldType(
110: "StringWithSelList", ValueType.STRING);
111: StaticSelectionList list = stringWithSelListField
112: .createStaticSelectionList();
113: list.createItem("foo");
114: list.createItem("bar");
115: stringWithSelListField.save();
116:
117: FieldType longField = schema.createFieldType("LongField",
118: ValueType.LONG);
119: longField.save();
120:
121: FieldType hierStringField = schema.createFieldType(
122: "HierStringField", ValueType.STRING, false, true);
123: hierStringField.save();
124:
125: DocumentType docType = schema.createDocumentType("Basic");
126: docType.addPartType(htmlContent, false);
127: docType.addPartType(binaryContent, false);
128: docType.addFieldType(stringField, false);
129: docType.addFieldType(mvStringField, false);
130: docType.addFieldType(stringWithSelListField, false);
131: docType.addFieldType(longField, false);
132: docType.addFieldType(hierStringField, false);
133: docType.save();
134:
135: DocumentType docType2 = schema.createDocumentType("Basic2");
136: docType2.addPartType(htmlContent, false);
137: docType2.addPartType(binaryContent, false);
138: docType2.addFieldType(stringField, false);
139: docType2.addFieldType(mvStringField, false);
140: docType2.addFieldType(stringWithSelListField, false);
141: docType2.addFieldType(longField, false);
142: docType2.addFieldType(hierStringField, false);
143: docType2.save();
144:
145: // Create collections
146: DocumentCollection collection1 = repository
147: .getCollectionManager()
148: .createCollection("Collection 1");
149: collection1.save();
150: DocumentCollection collection2 = repository
151: .getCollectionManager()
152: .createCollection("Collection 2");
153: collection2.save();
154:
155: // Create documents
156:
157: // a document with a bit of everything
158: Document doc1 = repository.createDocument("Document 1",
159: "Basic", "main", "en");
160: doc1.setField("StringField", "My string field");
161: doc1.setField("MVStringField", new String[] { "val1", "val2" });
162: doc1.setField("StringWithSelList", "foo");
163: doc1.setField("LongField", new Long(55));
164: doc1.setField("HierStringField", new HierarchyPath(
165: new String[] { "a", "b", "c" }));
166: doc1.setPart("HtmlContent", "text/xml",
167: "<html><body><p>Hello</p></body></html>"
168: .getBytes("UTF-8"));
169: doc1.setPart("BinaryContent", "application/octect-stream",
170: "abc".getBytes());
171: doc1.addLink("Link 1", "http://www.daisycms.org");
172: doc1.addLink("Link 2", "http://www.outerthought.org");
173: doc1.save();
174:
175: // a document with an existing translated version
176: Document doc2 = repository.createDocument("Document 2",
177: "Basic", "main", "en");
178: doc2.setField("StringField", "hello");
179: doc2.setField("LongField", new Long(60));
180: doc2.addToCollection(collection1);
181: doc2.save();
182:
183: Document doc2Nl = repository.createVariant(doc2.getId(), doc2
184: .getBranchId(), enLang.getId(), -1, doc2.getBranchId(),
185: nlLang.getId(), false);
186: doc2Nl.setField("StringField", "old hello");
187: doc2Nl.setField("LongField", new Long(59));
188: doc2Nl.addToCollection(collection2);
189: doc2Nl.save();
190:
191: doc2.changeDocumentType("Basic2");
192: doc2.save();
193:
194: // a document with an existing translated version which should be left unchanged
195: Document doc3 = repository.createDocument("Document 3",
196: "Basic", "main", "en");
197: doc3.setField("StringField", "value");
198: doc3.save();
199:
200: Document doc3Nl = repository.createVariant(doc3.getId(), doc3
201: .getBranchId(), enLang.getId(), -1, doc2.getBranchId(),
202: nlLang.getId(), false);
203: doc3Nl.setField("StringField", "value");
204: doc3Nl.save();
205:
206: final int DOCUMENT_COUNT = 3;
207:
208: //
209: // Common config
210: //
211:
212: String tmpDir = System.getProperty("java.io.tmpdir");
213: File expDir = new File(tmpDir, "daisy-tmimpexp-test");
214: if (expDir.exists())
215: deleteDirectory(expDir);
216:
217: TMConfig tmConfig = new TMConfig();
218: tmConfig.addLangIndepField("HierStringField");
219: tmConfig.addLangIndepField("Basic", "LongField");
220: tmConfig.addLangIndepPart("BinaryContent");
221:
222: //
223: // Export
224: //
225: DocumentSet documentSet = new QueryDocumentSet(
226: "select id where language = 'en'", repository);
227: ExportFile exportFile = new LocalExportFile(expDir);
228: Date exportTime = new Date();
229: ExportOptions exportOptions = ExportOptionsFactory
230: .getDefaultExportOptions();
231: MyExportListener listener = new MyExportListener();
232:
233: TMExporter.run(documentSet, exportFile, exportTime, repository,
234: exportOptions, listener, tmConfig);
235:
236: assertEquals(DOCUMENT_COUNT, listener.getSucceeded().size());
237:
238: //
239: // Modify export data
240: //
241: DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
242: .newInstance();
243: docBuilderFactory.setNamespaceAware(true);
244:
245: // Document 1
246: {
247: File doc1ExpFile = new File(expDir, doc1.getId()
248: + "~main.xml");
249: org.w3c.dom.Document doc1Doc = docBuilderFactory
250: .newDocumentBuilder().parse(doc1ExpFile);
251:
252: XPath xpath = LocalXPathFactory.get().newXPath();
253: SimpleNamespaceContext nsContext = new SimpleNamespaceContext();
254: nsContext.addPrefix("ie",
255: "http://outerx.org/daisy/1.0#tm-impexp");
256: xpath.setNamespaceContext(nsContext);
257:
258: Element stringFieldEl = (Element) xpath.evaluate(
259: "/ie:document/ie:field[@name='StringField']",
260: doc1Doc, XPathConstants.NODE);
261: setElementText(stringFieldEl, "translated value");
262:
263: Element helloEl = (Element) xpath
264: .evaluate(
265: "/ie:document/ie:part[@name='HtmlContent']//p[. = 'Hello']",
266: doc1Doc, XPathConstants.NODE);
267: setElementText(helloEl, "Hallo");
268:
269: saveDocument(doc1Doc, doc1ExpFile);
270: }
271:
272: // Document 2
273: {
274: File doc2ExpFile = new File(expDir, doc2.getId()
275: + "~main.xml");
276: org.w3c.dom.Document doc2Doc = docBuilderFactory
277: .newDocumentBuilder().parse(doc2ExpFile);
278:
279: XPath xpath = LocalXPathFactory.get().newXPath();
280: SimpleNamespaceContext nsContext = new SimpleNamespaceContext();
281: nsContext.addPrefix("ie",
282: "http://outerx.org/daisy/1.0#tm-impexp");
283: xpath.setNamespaceContext(nsContext);
284:
285: Element stringFieldEl = (Element) xpath.evaluate(
286: "/ie:document/ie:title", doc2Doc,
287: XPathConstants.NODE);
288: setElementText(stringFieldEl, "Document 2 translated");
289:
290: saveDocument(doc2Doc, doc2ExpFile);
291: }
292:
293: //
294: // Import
295: //
296: ImportFile importFile = new LocalImportFile(expDir);
297: ImportOptions importOptions = ImportOptionsFactory
298: .getDefaultImportOptions();
299: MyImportListener importListener = new MyImportListener();
300: assertNotNull(getClass()
301: .getClassLoader()
302: .getResourceAsStream(
303: "org/outerj/daisy/repository/test/resources/htmlcleaner.xml"));
304: HtmlCleanerTemplate htmlCleanerTempate = new HtmlCleanerFactory()
305: .buildTemplate(new InputSource(
306: getClass()
307: .getClassLoader()
308: .getResourceAsStream(
309: "org/outerj/daisy/repository/test/resources/htmlcleaner.xml")));
310:
311: TMImporter.run(importFile, null, repository, importOptions,
312: importListener, tmConfig, "nl", htmlCleanerTempate);
313:
314: // Normally there are no failures, but printing failure info is useful for debugging
315: System.out.println("Failed because access denied: "
316: + importListener.getFailedBecauseAccessDenied().size());
317: System.out.println("Failed because locked: "
318: + importListener.getFailedBecauseLockedDocument()
319: .size());
320: for (BaseImportListener.FailureInfo failureInfo : importListener
321: .getFailedDocuments()) {
322: System.out
323: .println("---------------------------------------------------------------------------");
324: System.out.println("Failure importing "
325: + failureInfo.getVariantKey());
326: System.out.println(failureInfo.getErrorDescription());
327: System.out.println(failureInfo.getStackTrace());
328: System.out
329: .println("---------------------------------------------------------------------------");
330: }
331: assertEquals(DOCUMENT_COUNT, importListener.getSucceeded()
332: .size());
333:
334: //
335: // Post-import tests
336: //
337: {
338: // Test document 1
339: {
340: Document transDoc = repository.getDocument(
341: doc1.getId(), "main", "nl", false);
342:
343: // language independent fields should be there...
344: assertTrue(transDoc.hasField("HierStringField"));
345: assertTrue(transDoc.hasField("LongField"));
346: assertTrue(transDoc.hasPart("BinaryContent"));
347: // but other not-exported non-language independent fields not...
348: assertFalse(transDoc.hasField("StringWithSelList"));
349:
350: assertEquals("translated value", transDoc.getField(
351: "StringField").getValue());
352: assertEquals("val2", ((Object[]) transDoc.getField(
353: "MVStringField").getValue())[1]);
354:
355: assertTrue(new String(transDoc.getPart("HtmlContent")
356: .getData()).indexOf("Hallo") > -1);
357:
358: VersionKey syncedWith = transDoc.getLastVersion()
359: .getSyncedWith();
360: assertNotNull(syncedWith);
361: assertEquals(enLang.getId(), syncedWith.getLanguageId());
362: assertEquals(1, syncedWith.getVersionId());
363:
364: Links links = transDoc.getLinks();
365: assertEquals("Link 1", links.getArray()[0].getTitle());
366: assertEquals("http://www.daisycms.org", links
367: .getArray()[0].getTarget());
368: assertEquals("Link 2", links.getArray()[1].getTitle());
369: }
370:
371: // Test document 2
372: {
373: Document transDoc = repository.getDocument(
374: doc2.getId(), "main", "nl", false);
375:
376: assertEquals("Document 2 translated", transDoc
377: .getDocumentName());
378: assertEquals(59l, transDoc.getField("LongField")
379: .getValue()); // should have stayed 59 since LongField is only language-independent for the "Basic" document type
380: assertEquals("hello", transDoc.getField("StringField")
381: .getValue());
382:
383: assertTrue(transDoc.inCollection(collection1));
384: assertFalse(transDoc.inCollection(collection2));
385:
386: // test document type change was synced
387: assertEquals(docType2.getId(), transDoc
388: .getDocumentTypeId());
389:
390: VersionKey syncedWith = transDoc.getLastVersion()
391: .getSyncedWith();
392: assertNotNull(syncedWith);
393: assertEquals(enLang.getId(), syncedWith.getLanguageId());
394: assertEquals(1, syncedWith.getVersionId());
395: }
396:
397: // Test document 3
398: {
399: Document transDoc = repository.getDocument(
400: doc3.getId(), "main", "nl", false);
401: assertEquals(1, transDoc.getLastVersionId());
402: }
403: }
404:
405: //
406: // Perform import a second time, no changes should happen now
407: //
408: importListener = new MyImportListener();
409: TMImporter.run(importFile, null, repository, importOptions,
410: importListener, tmConfig, "nl", htmlCleanerTempate);
411:
412: assertEquals(DOCUMENT_COUNT, importListener.getSucceeded()
413: .size());
414: for (BaseImportListener.SuccessInfo successInfo : importListener
415: .getSucceeded()) {
416: assertEquals(DocumentImportResult.NO_UPDATED_NEEDED,
417: successInfo.getResult());
418: }
419:
420: //
421: // Unset the 'synced with' of a version, it should be updated on import, even if
422: // no new version has been created
423: //
424: {
425: Document transDoc = repository.getDocument(doc2.getId(),
426: "main", "nl", true);
427: Version version = transDoc.getLastVersion();
428: version.setSyncedWith(null);
429: version.save();
430:
431: importListener = new MyImportListener();
432: TMImporter.run(importFile, null, repository, importOptions,
433: importListener, tmConfig, "nl", htmlCleanerTempate);
434:
435: assertEquals(DOCUMENT_COUNT, importListener.getSucceeded()
436: .size());
437: for (BaseImportListener.SuccessInfo successInfo : importListener
438: .getSucceeded()) {
439: if (successInfo.getVariantKey().getDocumentId().equals(
440: doc2.getId()))
441: assertEquals(DocumentImportResult.UPDATED,
442: successInfo.getResult());
443: else
444: assertEquals(
445: DocumentImportResult.NO_UPDATED_NEEDED,
446: successInfo.getResult());
447: }
448:
449: transDoc = repository.getDocument(doc2.getId(), "main",
450: "nl", true);
451: Version newVersion = transDoc.getLastVersion();
452: // the import should not have created a new version
453: assertEquals(version.getId(), newVersion.getId());
454: // but should have set the synced with
455: assertNotNull(newVersion.getSyncedWith());
456: }
457:
458: //
459: // Cleanup
460: //
461: deleteDirectory(expDir);
462: }
463:
464: private void deleteDirectory(File dir) {
465: File[] files = dir.listFiles();
466: for (File file : files) {
467: if (file.isDirectory()) {
468: deleteDirectory(file);
469: } else {
470: file.delete();
471: }
472: }
473: dir.delete();
474: }
475:
476: private void saveDocument(org.w3c.dom.Document document, File file)
477: throws TransformerException {
478: Transformer transformer = LocalTransformerFactory.get()
479: .newTransformer();
480: transformer.transform(new DOMSource(document),
481: new StreamResult(file));
482: }
483:
484: private void setElementText(Element element, String value) {
485: NodeList nodeList = element.getChildNodes();
486: for (int i = 0; i < nodeList.getLength(); i++) {
487: element.removeChild(nodeList.item(i));
488: }
489:
490: element.appendChild(element.getOwnerDocument().createTextNode(
491: value));
492: }
493:
494: private static class MyExportListener extends BaseExportListener {
495:
496: protected boolean includeFullStackTracesOfFailures() {
497: return true;
498: }
499:
500: protected String getStackTraceDisabledMessage() {
501: return null;
502: }
503:
504: public void info(String message) {
505: }
506:
507: public void startDocumentProgress(int total) {
508: }
509:
510: public void updateDocumentProgress(int current) {
511: }
512:
513: public void endDocumentProgress() {
514: }
515:
516: public boolean isInterrupted() {
517: return false;
518: }
519: }
520:
521: private static class MyImportListener extends BaseImportListener {
522:
523: protected boolean includeFullStackTracesOfFailures() {
524: return true;
525: }
526:
527: protected String getStackTraceDisabledMessage() {
528: return null;
529: }
530:
531: public void generateSchemaSaxFragment(
532: ContentHandler contentHandler) throws SAXException {
533: }
534:
535: public void startActivity(String name) {
536: }
537:
538: public void info(String message) {
539: }
540:
541: public void debug(String message) {
542: }
543:
544: public void startDocumentProgress(int total) {
545: }
546:
547: public void updateDocumentProgress(int current) {
548: }
549:
550: public void endDocumentProgress() {
551: }
552:
553: public SchemaLoadListener getSchemaListener() {
554: return null;
555: }
556:
557: public boolean isInterrupted() {
558: return false;
559: }
560: }
561:
562: protected abstract RepositoryManager getRepositoryManager()
563: throws Exception;
564: }
|