/** * JHOVE2 - Next-generation architecture for format-aware characterization * <p> * Copyright (c) 2009 by The Regents of the University of California, Ithaka * Harbors, Inc., and The Board of Trustees of the Leland Stanford Junior * University. All rights reserved. * </p> * <p> * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * </p> * <ul> * <li>Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer.</li> * <li>Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution.</li> * <li>Neither the name of the University of California/California Digital * Library, Ithaka Harbors/Portico, or Stanford University, nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission.</li> * </ul> * <p> * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * </p> */ package org.jhove2.module.format.shapefile; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.TreeMap; import java.util.Map.Entry; import org.geotools.data.shapefile.ShapefileFeatureExtractor; import org.jhove2.annotation.ReportableProperty; import org.jhove2.core.JHOVE2; import org.jhove2.core.JHOVE2Exception; import org.jhove2.core.Message; import org.jhove2.core.Message.Context; import org.jhove2.core.Message.Severity; import org.jhove2.core.format.Format; import org.jhove2.core.io.Input; import org.jhove2.core.source.ClumpSource; import org.jhove2.core.source.Source; import org.jhove2.module.format.BaseFormatModule; import org.jhove2.module.format.Validator; import org.jhove2.persist.FormatModuleAccessor; import com.sleepycat.persist.model.Persistent; /** * JHOVE2 Shapefile module. Used to extract information from shapefiles. * * @author rnanders */ @Persistent public class ShapefileModule extends BaseFormatModule implements Validator { /** Shapefile module version identifier. */ public static final String VERSION = "2.0.0"; /** Shapefile module release date. */ public static final String RELEASE = "2010-09-10"; /** Shapefile module rights statement. */ public static final String RIGHTS = "Copyright 2010 by The Board of " + "Trustees of the Leland Stanford Junior University. " + "Available under the terms of the BSD license."; /** Directory module validation coverage. */ public static final Coverage COVERAGE = Coverage.Selective; /** Shapefile validation status. */ protected Validity isValid; /** Features Extracted from the shapefile. */ protected ShapefileFeatures shapefileFeatures = new ShapefileFeatures(); /** Member files indexed by file type. */ protected TreeMap<String,File> memberFileMap = new TreeMap<String,File>(); /** * Instantiate a new <code>ShapefileModule</code>. * * @param format * Shapefile format */ public ShapefileModule(Format format, FormatModuleAccessor formatModuleAccessor) { super(VERSION, RELEASE, RIGHTS, format, formatModuleAccessor); this.isValid = Validity.Undetermined; } /** * Instantiate a new <code>ShapefileModule</code>. * * @param format * Shapefile format */ public ShapefileModule(Format format) { this(format, null); } public ShapefileModule(){ this(null, null); } /** * Parse an Shapefile source unit. * * @param jhove2 JHOVE2 framework * @param source Shapefile source unit * @param input Shapefile source input * @return 0 * @throws IOException If an I/O exception is raised reading the source unit * @throws JHOVE2Exception If parse is non-fuctional * @see {org.jhove2.module.format.Parser#parse(org.jhove2.core.JHOVE2, * org.jhove2.core.source.Source, core.jhove2.core.io.Input)} */ @Override public long parse(JHOVE2 jhove2, Source source, Input input) throws IOException, JHOVE2Exception { if (source instanceof ClumpSource) { inventoryMemberFiles((ClumpSource) source); String fileErr = verifyMemberFiles(); if (fileErr.length() > 0) { setErrorMessage(jhove2, source, fileErr); return -1; } ShapefileFeatureExtractor sfe = null; try { sfe = new ShapefileFeatureExtractor(memberFileMap.get("SHP")); sfe.extractFeatures(shapefileFeatures); } catch (Exception e) { setErrorMessage(jhove2, source, "Shapefile could not be parsed: " + e.getMessage()); return -1; } } return 0; } /** * Validate a Shapefile source unit. * * @param jhove2 JHOVE2 framework * @param source Source unit * @param input Source input * @return UTF-8 validation status * @throws JHOVE2Exception the jHOV e2 exception * @see org.jhove2.module.format.Validator#validate(org.jhove2.core.JHOVE2, * org.jhove2.core.source.Source, org.jhove2.core.io.Input) */ @Override public Validity validate(JHOVE2 jhove2, Source source, Input input) throws JHOVE2Exception { boolean hasConsistentRecordCount = false; long mainRecordCount = this.getShapefileFeatures().getShapefileRecordCount(); long dbfRecordCount = this.getShapefileFeatures().getDbfHeader().getRecordCount(); hasConsistentRecordCount = ((mainRecordCount > 0) && (mainRecordCount == dbfRecordCount)); if ((source.getMessages().size() == 0) && hasConsistentRecordCount) { this.isValid = Validity.True; } else { this.isValid = Validity.False; } return this.isValid; } /** Get Shapefile module validation coverage. * @return Shapefile module validation coverage */ @Override public Coverage getCoverage() { return COVERAGE; } /** * Get Shapefile validation status. * * @return Shapefile validation status * @see org.jhove2.module.format.Validator#isValid() */ @Override public Validity isValid() { return this.isValid; } /** * Extract a list of the member files. * * @param clump the clump * @throws JHOVE2Exception */ private void inventoryMemberFiles(ClumpSource clump) throws JHOVE2Exception { for (Source child : clump.getChildSources()) { File file = child.getFile(); String filename = file.getName().toUpperCase(); if (filename.endsWith(".SHP.XML")) { memberFileMap.put("SHP.XML", file); } else { String extension = filename.substring(filename.lastIndexOf(".") + 1); memberFileMap.put(extension, file); } File mainFile = memberFileMap.get("SHP"); if (mainFile != null) { String mainFilename = mainFile.getName(); shapefileFeatures.shapefileStem = mainFilename.substring(0, (mainFilename.length()-4)); } } shapefileFeatures.memberFiles = new ArrayList<String>(); for (Entry<String, File> entry : memberFileMap.entrySet()) { shapefileFeatures.memberFiles.add(entry.getKey() + " => " + entry.getValue().getName()); } } /** * Verify member files. * * @return the string */ private String verifyMemberFiles() { StringBuffer sb = new StringBuffer(); sb.append(verifyFile("SHP", 100)); sb.append(verifyFile("SHX", 100)); sb.append(verifyFile("DBF", 100)); return sb.toString(); } /** * Verify file existence and minimum size. * Geotools will enter infinte loop trying to parse too small a file * * @param type the type * @param minSize the minimum size * @return the string */ private String verifyFile(String type, int minSize) { File file = memberFileMap.get(type); if (!file.exists()) { return type + " file does not exist (" + file.getPath() + "); "; } if (file.length() < minSize) { return type + " file smaller than " + minSize + " bytes (" + file.getPath() + "; "; } return ""; } /** * Gets the extracted shapefile features. * * @return the reportable properties for the shapefile */ @ReportableProperty(order = 1, value ="Shapefile Features") public ShapefileFeatures getShapefileFeatures() { return shapefileFeatures; } /** * Sets the error message. * * @param jhove2 the JHOVE2 object * @param source the Source object * @param errMsg the error message * @throws JHOVE2Exception if getConfigInfo were to fail */ private void setErrorMessage(JHOVE2 jhove2, Source source, String errMsg) throws JHOVE2Exception { Object[]messageArgs = new Object[]{errMsg}; source.addMessage(new Message(Severity.ERROR, Context.PROCESS, "org.jhove2.module.format.shapefile.ShapefileModule.parseMessage", messageArgs, jhove2.getConfigInfo())); } }