/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 Adempiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* *
* Copyright (C) *
* 2004 Robert KLEIN. robeklein@hotmail.com *
* Contributor(s): Low Heng Sin hengsin@avantz.com *
* Teo Sarca teo.sarca@arhipac.ro, SC ARHIPAC SERVICE SRL *
*****************************************************************************/
package org.adempiere.pipo2;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import org.compiere.model.MClient;
import org.compiere.model.MSysConfig;
import org.compiere.model.MTable;
import org.compiere.tools.FileUtil;
import org.compiere.util.CLogger;
import org.compiere.util.Trx;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.adempiere.exceptions.AdempiereException;
/**
* Convert AD to XML
*
* @author Robert Klein
* @version $Id: PackOut.java,v 1.0
*
* Contributor: William G. Heath - Export of workflows and dynamic validations
*
* @author Teo Sarca, SC ARHIPAC SERVICE SRL
*
BF [ 1819315 ] PackOut: fix xml indentation not working
* BF [ 1819319 ] PackOut: use just active AD_Package_Exp_Detail lines
*/
public class PackOut
{
/*** 1.0.0 **/
public final static String PackOutVersion = "100";
private final static CLogger log = CLogger.getCLogger(PackOut.class);
private String packageDirectory;
private int blobCount = 0;
private IHandlerRegistry handlerRegistry = new OSGiHandlerRegistry();
private PackoutItem packoutItem;
private String trxName;
private PackoutDocument packoutDocument;
private int processedCount;
private String exportFile;
private String packoutDirectory;
private PIPOContext pipoContext = new PIPOContext();
private Timestamp fromDate;
private boolean isExportDictionaryEntity = false;
public static final int MAX_OFFICIAL_ID = MTable.MAX_OFFICIAL_ID;
public static final String PACKOUT_BLOB_FILE_EXTENSION = ".dat";
public static void addTextElement(TransformerHandler handler, String qName, String text, AttributesImpl atts) throws SAXException {
handler.startElement("", "", qName, atts);
append(handler, text);
handler.endElement("", "", qName);
}
private static void append(TransformerHandler handler, String str) throws SAXException
{
char[] contents = str != null ? str.toCharArray() : new char[0];
handler.characters(contents,0,contents.length);
}
/**
* Start the transformation to XML
* @param packoutDirectory
* @param destinationPath
* @param packoutDocument
* @param packoutItems
* @param trxName
* @throws java.lang.Exception
*/
public void export(String packoutDirectory, String destinationPath, PackoutDocument packoutDocument, List packoutItems, String trxName) throws java.lang.Exception
{
this.packoutDirectory = packoutDirectory;
this.packoutDocument = packoutDocument;
this.trxName = trxName;
initContext();
OutputStream docStream = null;
OutputStream packoutStream = null;
processedCount = 0;
try {
packageDirectory = packoutDirectory+ packoutDocument.getPackageName();
File docDirectoryFile = new File(packageDirectory+File.separator+"doc"+File.separator);
if (!docDirectoryFile.exists()) {
boolean success = docDirectoryFile.mkdirs();
if (!success) {
throw new AdempiereException("Failed to create directory for pack out. " + packageDirectory+File.separator+"doc"+File.separator);
}
}
String docFileName = packageDirectory+File.separator+"doc"+File.separator+packoutDocument.getPackageName()+"Doc.xml";
docStream = new FileOutputStream (docFileName, false);
TransformerHandler docHandler = createDocHandler(docStream);
String packoutFileName = packageDirectory+File.separator+ "dict"+File.separator+"PackOut.xml";
packoutStream = new FileOutputStream (packoutFileName, false);
TransformerHandler packoutHandler = createPackoutHandler(packoutStream);
for(PackoutItem packoutItem : packoutItems){
this.packoutItem = packoutItem;
String type = packoutItem.getType();
ElementHandler handler = handlerRegistry.getHandler(type);
if (handler != null)
handler.packOut(this,packoutHandler,docHandler,packoutItem.getRecordId(),packoutItem.getUUID());
else
throw new IllegalArgumentException("Packout handler not found for type " + type);
processedCount++;
}
packoutHandler.endElement("","","idempiere");
packoutHandler.endDocument();
docHandler.endElement("","","idempiereDocument");
docHandler.endDocument();
}
catch (Exception e)
{
log.log(Level.SEVERE,e.getLocalizedMessage(), e);
throw e;
}
finally
{
// Close streams - teo_sarca [ 1704762 ]
if (docStream != null)
try {
docStream.close();
} catch (Exception e) {}
if (packoutStream != null)
try {
packoutStream.close();
} catch (Exception e) {}
}
//create compressed packages
//set the files
File srcFolder = new File(packoutDirectory);
File destZipFile = null;
if (destinationPath != null && destinationPath.trim().length() > 0)
{
destZipFile = new File(destinationPath);
} else {
destZipFile = new File(packageDirectory+".zip");
}
//delete the old packages if necessary
destZipFile.delete();
//create the compressed packages
String includesdir = packoutDocument.getPackageName() + File.separator +"**";
Zipper.zipFolder(srcFolder, destZipFile, includesdir);
exportFile = destZipFile.getAbsolutePath();
FileUtil.deleteFolderRecursive(new File(packageDirectory));
} // doIt
private TransformerHandler createPackoutHandler(
OutputStream packoutStream) throws UnsupportedEncodingException, TransformerConfigurationException, SAXException {
StreamResult packoutStreamResult = new StreamResult(new OutputStreamWriter(packoutStream,"UTF-8"));
SAXTransformerFactory packoutFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
//indent-number attribute support is not guarantee
try {
packoutFactory.setAttribute("indent-number", Integer.valueOf(4));
} catch (Exception e) {}
TransformerHandler packoutHandler = packoutFactory.newTransformerHandler();
Transformer packoutTransformer = packoutHandler.getTransformer();
packoutTransformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
packoutTransformer.setOutputProperty(OutputKeys.INDENT,"yes");
//indent-amount property support is not guarantee
try {
packoutTransformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount","4");
} catch (Exception e) {}
packoutHandler.setResult(packoutStreamResult);
packoutHandler.startDocument();
AttributesImpl atts = new AttributesImpl();
atts.addAttribute("","","Name","CDATA",packoutDocument.getPackageName());
atts.addAttribute("","","Version","CDATA",packoutDocument.getPackageVersion());
atts.addAttribute("","","idempiereVersion","CDATA",emptyIfNull(packoutDocument.getAdempiereVersion()));
atts.addAttribute("","","DataBaseVersion","CDATA",emptyIfNull(packoutDocument.getDatabaseVersion()));
atts.addAttribute("","","Description","CDATA",emptyIfNull(packoutDocument.getDescription()));
atts.addAttribute("","","Author","CDATA",emptyIfNull(packoutDocument.getAuthor()));
atts.addAttribute("","","AuthorEmail","CDATA",emptyIfNull(packoutDocument.getAuthorEmail()));
atts.addAttribute("","","CreatedDate","CDATA",packoutDocument.getCreated().toString());
atts.addAttribute("","","UpdatedDate","CDATA",packoutDocument.getUpdated().toString());
atts.addAttribute("","","PackOutVersion","CDATA",PackOutVersion);
atts.addAttribute("","","UpdateDictionary","CDATA", isExportDictionaryEntity ? "true" : "false");
MClient client = MClient.get(pipoContext.ctx);
StringBuilder sb = new StringBuilder ()
.append(client.get_ID())
.append("-")
.append(client.getValue())
.append("-")
.append(client.getName());
atts.addAttribute("", "", "Client", "CDATA", sb.toString());
if (client.getAD_Client_UU() == null)
throw new IllegalStateException("2Pack requires UUID on AD_Client");
atts.addAttribute("", "", "AD_Client_UU", "CDATA", client.getAD_Client_UU());
packoutHandler.startElement("","","idempiere",atts);
return packoutHandler;
}
private String emptyIfNull(String input) {
return input != null ? input : "";
}
private TransformerHandler createDocHandler(OutputStream docStream) throws UnsupportedEncodingException, TransformerConfigurationException, SAXException {
StreamResult docStreamResult = new StreamResult(new OutputStreamWriter(docStream,"UTF-8"));
SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
//indent-number attribute support is not guarantee
try {
transformerFactory.setAttribute("indent-number", Integer.valueOf(4));
} catch (Exception e) {}
TransformerHandler docHandler = transformerFactory.newTransformerHandler();
Transformer transformer = docHandler.getTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT,"yes");
//indent-amount property support is not guarantee
try {
transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount","4");
} catch (Exception e) {}
docHandler.setResult(docStreamResult);
docHandler.startDocument();
docHandler.processingInstruction("xml-stylesheet","type=\"text/css\" href=\"idempiereDocument.css\"");
AttributesImpl atts = new AttributesImpl();
docHandler.startElement("","","idempiereDocument",atts);
addTextElement(docHandler, "header", packoutDocument.getPackageName()+" Package Description", atts);
addTextElement(docHandler, "H1", "Package Name:", atts);
addTextElement(docHandler, "packagename", packoutDocument.getPackageName(), atts);
addTextElement(docHandler, "H1", "Author:", atts);
addTextElement(docHandler, "Name:", packoutDocument.getAuthor(), atts);
addTextElement(docHandler, "H1", "Email Address:", atts);
addTextElement(docHandler, "Email", packoutDocument.getAuthorEmail(), atts);
addTextElement(docHandler, "H1", "Created:", atts);
addTextElement(docHandler, "Date", packoutDocument.getCreated().toString(), atts);
addTextElement(docHandler, "H1", "Updated:", atts);
addTextElement(docHandler, "Date", packoutDocument.getUpdated().toString(), atts);
addTextElement(docHandler, "H1", "Description:", atts);
addTextElement(docHandler, "description", packoutDocument.getDescription(), atts);
addTextElement(docHandler, "H1", "Instructions:", atts);
addTextElement(docHandler, "instructions", packoutDocument.getInstructions(), atts);
addTextElement(docHandler, "H1", "Files in Package:", atts);
addTextElement(docHandler, "file", "File: PackOut.xml", atts);
addTextElement(docHandler, "filedirectory", "Directory: \\dict\\", atts);
addTextElement(docHandler, "filenotes", "Notes: Contains all application/object settings for package", atts);
MClient client = MClient.get(pipoContext.ctx);
StringBuilder sb = new StringBuilder ()
.append(client.get_ID())
.append("-")
.append(client.getValue())
.append("-")
.append(client.getName());
addTextElement(docHandler, "H1", "Client:", atts);
addTextElement(docHandler, "Client", sb.toString(), atts);
File packageDictDirFile = new File(packageDirectory+File.separator+ "dict"+File.separator);
if (!packageDictDirFile.exists()) {
boolean success = packageDictDirFile.mkdirs();
if (!success)
throw new AdempiereException("Failed to create directory. " + packageDirectory+File.separator+ "dict"+File.separator);
}
return docHandler;
}
private void initContext() {
if (trxName != null)
pipoContext.trx = Trx.get(trxName, true);
pipoContext.ctx.setProperty("isHandleTranslations", MSysConfig.getValue(MSysConfig.TWOPACK_HANDLE_TRANSLATIONS));
pipoContext.packOut = this;
}
/**
* @param sourceName
* @param destName
*/
public void copyFile (String sourceName, String destName ) {
InputStream source = null; // Stream for reading from the source file.
OutputStream copy= null; // Stream for writing the copy.
boolean force; // This is set to true if the "-f" option
// is specified on the command line.
int byteCount; // Number of bytes copied from the source file.
force = true;
try {
source = new FileInputStream(sourceName);
} catch (FileNotFoundException e) {
System.out.println("Can't find file \"" + sourceName + "\".");
return;
}
try {
File file = new File(destName);
if (file.exists() && force == false) {
System.out.println("Output file exists. Use the -f option to replace it.");
return;
}
try {
copy = new FileOutputStream(destName, false);
} catch (IOException e) {
System.out.println("Can't open output file \""
+ destName + "\".");
return;
}
byteCount = 0;
try {
while (true) {
int data = source.read();
if (data < 0)
break;
copy.write(data);
byteCount++;
}
source.close();
copy.close();
System.out.println("Successfully copied " + byteCount + " bytes.");
} catch (Exception e) {
System.out.println("Error occurred while copying. "+ byteCount + " bytes copied.");
System.out.println(e.toString());
}
} finally {
if (source != null) {
try {
source.close();
} catch (IOException e) {}
}
if (copy != null) {
try {
copy.close();
} catch (IOException e) {}
}
}
}
public PIPOContext getCtx() {
return pipoContext;
}
/**
* @param data
* @return
* @throws IOException
*/
public String writeBlob(byte[] data) throws IOException {
blobCount++;
String fileName = blobCount + PACKOUT_BLOB_FILE_EXTENSION;
File path = new File(packageDirectory+File.separator+"blobs"+File.separator);
path.mkdirs();
File file = new File(path, fileName);
FileOutputStream os = null;
try {
os = new FileOutputStream(file);
os.write(data);
os.flush();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {}
}
}
return fileName;
}
/**
* @return MPackageExpDetail
*/
public PackoutItem getCurrentPackoutItem() {
return packoutItem;
}
/**
*
* @return PackoutDocument
*/
public PackoutDocument getPackoutDocument() {
return packoutDocument;
}
public String getPackoutDirectory() {
return packoutDirectory;
}
/**
* @param name
* @return ElementHandler
*/
public ElementHandler getHandler(String name) {
return handlerRegistry.getHandler(name);
}
/**
* @return number of records exported
*/
public int getExportCount() {
return processedCount;
}
/**
* @return absolute path for export file
*/
public String getExportFile() {
return exportFile;
}
/**
* @param fromDate
*/
public void setFromDate(Timestamp fromDate) {
this.fromDate = fromDate;
}
/**
* @return from date
*/
public Timestamp getFromDate() {
return fromDate;
}
/**
* @param ctx
*/
public void setCtx(Properties ctx) {
pipoContext.ctx = ctx;
}
private List processedRecords = new ArrayList();
public boolean isExported(String key) {
if (processedRecords.contains(key))
return true;
processedRecords.add(key);
return false;
}
public boolean isExportDictionaryEntity() {
return isExportDictionaryEntity;
}
public void setExportDictionaryEntity(boolean isExportDictionaryEntity) {
this.isExportDictionaryEntity = isExportDictionaryEntity;
}
} // PackOut