blob: a4c739c598bc215f31b1779d686ff8a7bfea77cd [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2008 The University of York.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.epsilon.emc.spreadsheets.excel;
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.util.Collection;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.eclipse.epsilon.common.module.ModuleElement;
import org.eclipse.epsilon.common.util.StringProperties;
import org.eclipse.epsilon.emc.spreadsheets.ISpreadsheetMetadata;
import org.eclipse.epsilon.emc.spreadsheets.ISpreadsheetMetadata.SpreadsheetWorksheetMetadata;
import org.eclipse.epsilon.emc.spreadsheets.MetadataXMLParser;
import org.eclipse.epsilon.emc.spreadsheets.SpreadsheetModel;
import org.eclipse.epsilon.emc.spreadsheets.SpreadsheetRow;
import org.eclipse.epsilon.emc.spreadsheets.SpreadsheetWorksheet;
import org.eclipse.epsilon.eol.EolModule;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
import org.eclipse.epsilon.eol.execute.context.IEolContext;
import org.eclipse.epsilon.eol.execute.context.Variable;
import org.eclipse.epsilon.eol.models.IRelativePathResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class ExcelModel extends SpreadsheetModel
{
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelModel.class);
/*
* Public identifiers used for receiving parameters from Epsilon Development Tools
*/
public static final String SPREADSHEET_FILE = "SPREADSHEET_FILE";
public static final String CONFIGURATION_FILE = "CONFIGURATION_FILE";
public static final String SPREADSHEET_PASSWORD = "SPREADSHEET_PASSWORD";
protected Workbook workbook;
private File spreadsheetFile;
private File configurationFile;
private Document configurationDoc;
private String password;
public ExcelModel()
{
super();
this.spreadsheetFile = null;
this.configurationFile = null;
this.configurationDoc = null;
this.password = null;
}
/*
* Password for test files out of box is "eps"
*/
public static void main(final String[] args) throws Exception
{
ExcelModel model = new ExcelModel();
if (args.length == 4)
{
model.setSpreadsheetFile(args[0]);
model.setConfigurationFile(args[1]);
model.setName(args[2]);
model.setPassword(args[3]);
model.setStoredOnDisposal(true);
}
model.load();
System.out.println("*** Executing EOL Code...");
EolModule module = new EolModule();
module.parse("Sheet1.all.println();");
// module.parse("Problems.all.println(); Problems.all.reqId.println(); Problems.all.reqId.name.println();"); //
// module.parse("Requirement.all.println(); new Requirement(Map{'c_0'='12345'});"); //
// module.parse("var s = new Sheet1(Map{'c_0'='12345'}); s.a.println();"); //
// module.parse("var first = Sheet1.all.first(); first.println(); new Sheet2(Map{'c_0'='hi'});");
module.getContext().getModelRepository().addModel(model);
module.execute();
model.store();
}
public void setSpreadsheetFile(final String pathToSpreadsheet)
{
LOGGER.debug("Inside setSpreadsheetFile() method");
LOGGER.debug("File path: '" + pathToSpreadsheet + "'");
if (StringUtils.isNotEmpty(pathToSpreadsheet))
{
this.spreadsheetFile = new File(pathToSpreadsheet);
}
else
{
final String message = "Spreadsheet File must be provided";
LOGGER.error(message);
throw new IllegalArgumentException(message);
}
}
public void setConfigurationFile(final String configurationFilePath) throws ParserConfigurationException,
SAXException, IOException
{
if (StringUtils.isNotBlank(configurationFilePath))
{
this.configurationFile = new File(configurationFilePath);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
this.configurationDoc = documentBuilder.parse(this.configurationFile);
}
}
/*
* For XLSX spreadsheets protected by password
*/
public void setPassword(final String password)
{
this.password = password;
}
@Override
public void load(final StringProperties properties, final IRelativePathResolver resolver)
throws EolModelLoadingException
{
super.load(properties, resolver);
try
{
final String spreadsheetFilePath = properties.getProperty(ExcelModel.SPREADSHEET_FILE);
this.setSpreadsheetFile(resolver.resolve(spreadsheetFilePath));
final String configurationFilePath = properties.getProperty(ExcelModel.CONFIGURATION_FILE);
if (StringUtils.isNotEmpty(configurationFilePath))
{
this.setConfigurationFile(resolver.resolve(configurationFilePath));
}
this.setPassword(properties.getProperty(ExcelModel.SPREADSHEET_PASSWORD));
}
catch (Exception e)
{
LOGGER.error(e.getMessage());
throw new EolModelLoadingException(e, this);
}
this.load();
}
@Override
protected void loadSpreadsheet() throws Exception
{
this.workbook = this.getWorkbook();
for (int i = 0; i < this.workbook.getNumberOfSheets(); i++)
{
final Sheet sheet = this.workbook.getSheetAt(i);
final ExcelWorksheet worksheet = new ExcelWorksheet(this, sheet, true);
LOGGER.debug("Loaded worksheet from file: '" + worksheet.getName() + "'");
this.addWorksheet(worksheet);
}
}
private Workbook getWorkbook() throws IOException
{
if (this.getIsXlsxFile())
{
return new XSSFWorkbook(this.getFileInputStream());
}
else
{
return new HSSFWorkbook(this.getFileInputStream());
}
}
private InputStream getFileInputStream() throws FileNotFoundException
{
if (StringUtils.isNotBlank(this.password))
{
if (this.getIsXlsxFile())
{
return this.getProtectedInputStreamForXlsx();
}
else
{
final String message = "Cannot load password protected XLS files";
LOGGER.error(message);
throw new UnsupportedOperationException(message);
}
}
return new FileInputStream(this.spreadsheetFile);
}
private boolean getIsXlsxFile()
{
return StringUtils.endsWithIgnoreCase(this.spreadsheetFile.toString(), "xlsx");
}
private InputStream getProtectedInputStreamForXlsx()
{
try
{
final POIFSFileSystem fileSystem = new POIFSFileSystem(new FileInputStream(this.spreadsheetFile));
final EncryptionInfo encryptionInfo = new EncryptionInfo(fileSystem);
final Decryptor decryptor = Decryptor.getInstance(encryptionInfo);
decryptor.verifyPassword(this.password);
return decryptor.getDataStream(fileSystem);
}
catch (Exception e)
{
final String message = "Failed to open file with the given password: " + e.getMessage();
LOGGER.error(message);
throw new RuntimeException(message);
}
}
@Override
protected ISpreadsheetMetadata getSpreadsheetMetadata()
{
return new MetadataXMLParser(this.configurationDoc);
}
@Override
protected boolean isMetadataConfigurationDefined()
{
return this.configurationFile != null && this.configurationDoc != null;
}
@Override
protected ExcelWorksheet createWorksheet(final SpreadsheetWorksheetMetadata worksheetMetadata)
{
final Sheet sheet = this.workbook.createSheet(worksheetMetadata.getName());
return new ExcelWorksheet(this, sheet, false);
}
@Override
public boolean store(String location) {
File spreadsheetFile = this.spreadsheetFile;
this.spreadsheetFile = new File(location);
boolean result = store();
this.spreadsheetFile = spreadsheetFile;
return result;
}
@Override
public boolean store()
{
try
{
this.deleteNonexistentWorksheets();
this.writeFile();
this.encryptFile();
return true;
}
catch (Exception e)
{
LOGGER.error("Failed to write to workbook '" + this.name + "' " + e.getMessage());
return false;
}
}
/*
* Apache POI will create an actual worksheet whenever a new Sheet object is created from whatever has been defined
* in the configuration file when using EOL. This method removes any worksheet that has been defined in
* configuration file but has not actually been referenced in the program or defined to be created on load.
*/
private void deleteNonexistentWorksheets()
{
for (final SpreadsheetWorksheet worksheet : this.worksheets)
{
if (worksheet.getDoesNotExistInSpreadsheet())
{
this.deleteWorksheet(worksheet);
}
}
}
private void writeFile() throws IOException
{
this.writeFile(null);
}
private void writeFile(final POIFSFileSystem fileSystem) throws IOException
{
try (FileOutputStream fileOutputStream = new FileOutputStream(this.spreadsheetFile))
{
if (fileSystem == null)
{
this.workbook.write(fileOutputStream);
}
else
{
fileSystem.writeFilesystem(fileOutputStream);
}
}
}
private void encryptFile() throws Exception
{
if (StringUtils.isNotBlank(this.password))
{
if (this.getIsXlsxFile())
{
final POIFSFileSystem fs = new POIFSFileSystem();
final EncryptionInfo info = new EncryptionInfo(fs, EncryptionMode.agile);
final Encryptor enc = info.getEncryptor();
enc.confirmPassword(this.password);
try (OPCPackage opc = OPCPackage.open(this.spreadsheetFile, PackageAccess.READ_WRITE))
{
OutputStream os = enc.getDataStream(fs);
opc.save(os);
}
this.writeFile(fs);
}
}
}
@Override
public void deleteWorksheet(final SpreadsheetWorksheet worksheet)
{
final int worksheetIndex = this.workbook.getSheetIndex(((ExcelWorksheet) worksheet).sheet);
this.workbook.removeSheetAt(worksheetIndex);
}
@Override
public Collection<SpreadsheetRow> find(Variable iterator, ModuleElement ast, IEolContext context) throws EolRuntimeException
{
throw new UnsupportedOperationException();
}
}