/* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ package org.apache.poi.xssf.usermodel; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import javax.xml.namespace.QName; import org.apache.poi.POIXMLDocument; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CommentsSource; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.DataFormat; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Palette; import org.apache.poi.ss.usermodel.PictureData; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.SharedStringSource; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.StylesSource; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.util.SheetReferences; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.model.Control; import org.apache.poi.xssf.model.Drawing; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.XSSFModel; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; import org.openxml4j.exceptions.InvalidFormatException; import org.openxml4j.opc.Package; import org.openxml4j.opc.PackagePart; import org.openxml4j.opc.PackagePartName; import org.openxml4j.opc.PackageRelationship; import org.openxml4j.opc.PackageRelationshipCollection; import org.openxml4j.opc.PackageRelationshipTypes; import org.openxml4j.opc.PackagingURIHelper; import org.openxml4j.opc.TargetMode; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookView; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookViews; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedNames; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDialogsheet; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument; import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument; public class XSSFWorkbook extends POIXMLDocument implements Workbook { /** Are we a normal workbook, or a macro enabled one? */ private boolean isMacroEnabled = false; private CTWorkbook workbook; private List sheets = new LinkedList(); private List namedRanges = new LinkedList(); private SharedStringSource sharedStringSource; private StylesSource stylesSource; private MissingCellPolicy missingCellPolicy = Row.RETURN_NULL_AND_BLANK; private static POILogger log = POILogFactory.getLogger(XSSFWorkbook.class); public XSSFWorkbook() { this.workbook = CTWorkbook.Factory.newInstance(); CTBookViews bvs = this.workbook.addNewBookViews(); CTBookView bv = bvs.addNewWorkbookView(); bv.setActiveTab(0); this.workbook.addNewSheets(); // We always require styles and shared strings sharedStringSource = new SharedStringsTable(); stylesSource = new StylesTable(); } public XSSFWorkbook(String path) throws IOException { this(openPackage(path)); } public XSSFWorkbook(Package pkg) throws IOException { super(pkg); try { WorkbookDocument doc = WorkbookDocument.Factory.parse(getCorePart().getInputStream()); this.workbook = doc.getWorkbook(); // Are we macro enabled, or just normal? isMacroEnabled = getCorePart().getContentType().equals(XSSFRelation.MACROS_WORKBOOK.getContentType()); try { // Load shared strings this.sharedStringSource = (SharedStringSource) XSSFRelation.SHARED_STRINGS.load(getCorePart()); } catch(Exception e) { throw new IOException("Unable to load shared strings - " + e.toString()); } try { // Load styles source this.stylesSource = (StylesSource) XSSFRelation.STYLES.load(getCorePart()); } catch(Exception e) { e.printStackTrace(); throw new IOException("Unable to load styles - " + e.toString()); } // Load individual sheets for (CTSheet ctSheet : this.workbook.getSheets().getSheetArray()) { PackagePart part = getPackagePart(ctSheet); if (part == null) { log.log(POILogger.WARN, "Sheet with name " + ctSheet.getName() + " and r:id " + ctSheet.getId()+ " was defined, but didn't exist in package, skipping"); continue; } // Load child streams of the sheet ArrayList childModels; CommentsSource comments = null; ArrayList drawings; ArrayList controls; try { // Get the comments for the sheet, if there are any childModels = XSSFRelation.SHEET_COMMENTS.loadAll(part); if(childModels.size() > 0) { comments = (CommentsSource)childModels.get(0); } // Get the drawings for the sheet, if there are any drawings = (ArrayList)XSSFRelation.VML_DRAWINGS.loadAll(part); // Get the activeX controls for the sheet, if there are any controls = (ArrayList)XSSFRelation.ACTIVEX_CONTROLS.loadAll(part); } catch(Exception e) { throw new RuntimeException("Unable to construct child part",e); } // Now create the sheet WorksheetDocument worksheetDoc = WorksheetDocument.Factory.parse(part.getInputStream()); XSSFSheet sheet = new XSSFSheet(ctSheet, worksheetDoc.getWorksheet(), this, comments, drawings, controls); this.sheets.add(sheet); // Process external hyperlinks for the sheet, // if there are any PackageRelationshipCollection hyperlinkRels = part.getRelationshipsByType(XSSFRelation.SHEET_HYPERLINKS.getRelation()); sheet.initHyperlinks(hyperlinkRels); // Get the embeddings for the workbook for(PackageRelationship rel : part.getRelationshipsByType(XSSFRelation.OLEEMBEDDINGS.getRelation())) embedds.add(getTargetPart(rel)); // TODO: Add this reference to each sheet as well for(PackageRelationship rel : part.getRelationshipsByType(XSSFRelation.PACKEMBEDDINGS.getRelation())) embedds.add(getTargetPart(rel)); } } catch (XmlException e) { throw new IOException(e.toString()); } catch (InvalidFormatException e) { throw new IOException(e.toString()); } // Process the named ranges if(workbook.getDefinedNames() != null) { for(CTDefinedName ctName : workbook.getDefinedNames().getDefinedNameArray()) { namedRanges.add(new XSSFName(ctName, this)); } } } protected CTWorkbook getWorkbook() { return this.workbook; } /** * Get the PackagePart corresponding to a given sheet. * * @param ctSheet The sheet * @return A PackagePart, or null if no matching part found. * @throws InvalidFormatException */ private PackagePart getPackagePart(CTSheet ctSheet) throws InvalidFormatException { PackageRelationship rel = this.getCorePart().getRelationship(ctSheet.getId()); if (rel == null) { log.log(POILogger.WARN, "No relationship found for sheet " + ctSheet.getId() + " - core part has " + this.getCorePart().getRelationships().size() + " relations defined"); return null; } return getTargetPart(rel); } public int addPicture(byte[] pictureData, int format) { // TODO Auto-generated method stub return 0; } public int addSSTString(String string) { // TODO Auto-generated method stub return 0; } public Sheet cloneSheet(int sheetNum) { XSSFSheet srcSheet = sheets.get(sheetNum); String srcName = getSheetName(sheetNum); if (srcSheet != null) { XSSFSheet clonedSheet = srcSheet.cloneSheet(); sheets.add(clonedSheet); CTSheet newcts = this.workbook.getSheets().addNewSheet(); newcts.set(clonedSheet.getSheet()); int i = 1; while (true) { //Try and find the next sheet name that is unique String name = srcName; String index = Integer.toString(i++); if (name.length() + index.length() + 2 < 31) { name = name + "("+index+")"; } else { name = name.substring(0, 31 - index.length() - 2) + "(" +index + ")"; } //If the sheet name is unique, then set it otherwise move on to the next number. if (getSheetIndex(name) == -1) { setSheetName(sheets.size() - 1, name); break; } } return clonedSheet; } return null; } public CellStyle createCellStyle() { return new XSSFCellStyle(stylesSource); } public DataFormat createDataFormat() { return getCreationHelper().createDataFormat(); } public Font createFont() { // TODO Auto-generated method stub return null; } public XSSFName createName() { XSSFName name = new XSSFName(this); namedRanges.add(name); return name; } public Sheet createSheet() { return createSheet(null); } public Sheet createSheet(String sheetname) { return createSheet(sheetname, null); } public Sheet createSheet(String sheetname, CTWorksheet worksheet) { CTSheet sheet = addSheet(sheetname); XSSFWorksheet wrapper = new XSSFWorksheet(sheet, worksheet, this); this.sheets.add(wrapper); return wrapper; } public Sheet createDialogsheet(String sheetname, CTDialogsheet dialogsheet) { CTSheet sheet = addSheet(sheetname); XSSFDialogsheet wrapper = new XSSFDialogsheet(sheet, dialogsheet, this); this.sheets.add(wrapper); return wrapper; } private CTSheet addSheet(String sheetname) { CTSheet sheet = workbook.getSheets().addNewSheet(); if (sheetname != null) { sheet.setName(sheetname); } return sheet; } public void dumpDrawingGroupRecords(boolean fat) { // TODO Auto-generated method stub } public Font findFont(short boldWeight, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline) { // TODO Auto-generated method stub return null; } public List getAllEmbeddedObjects() { // TODO Auto-generated method stub return null; } public List getAllPictures() { // In OOXML pictures are referred to in sheets List pictures = new LinkedList(); for (CTSheet ctSheet : this.workbook.getSheets().getSheetArray()) { try { PackagePart sheetPart = getPackagePart(ctSheet); if (sheetPart == null) { continue; } PackageRelationshipCollection prc = sheetPart.getRelationshipsByType(XSSFRelation.DRAWINGS.getRelation()); for (PackageRelationship rel : prc) { PackagePart drawingPart = getTargetPart(rel); PackageRelationshipCollection prc2 = drawingPart.getRelationshipsByType(XSSFRelation.IMAGES.getRelation()); for (PackageRelationship rel2 : prc2) { PackagePart imagePart = getTargetPart(rel2); XSSFPictureData pd = new XSSFPictureData(imagePart); pictures.add(pd); } } } catch (InvalidFormatException e) { throw new RuntimeException(e.getMessage(), e); } } return pictures; } public boolean getBackupFlag() { // TODO Auto-generated method stub return false; } public byte[] getBytes() { // TODO Auto-generated method stub return null; } public CellStyle getCellStyleAt(short idx) { // TODO Auto-generated method stub return null; } public Palette getCustomPalette() { // TODO Auto-generated method stub return null; } public short getDisplayedTab() { // TODO Auto-generated method stub return 0; } public Font getFontAt(short idx) { // TODO Auto-generated method stub return null; } public XSSFName getNameAt(int index) { return namedRanges.get(index); } public String getNameName(int index) { return getNameAt(index).getNameName(); } public int getNameIndex(String name) { for(int i=0; i 0) { CTDefinedNames names = CTDefinedNames.Factory.newInstance(); CTDefinedName[] nr = new CTDefinedName[namedRanges.size()]; for(int i=0; i