/** * Copyright 2010 The University of North Carolina at Chapel Hill * * Licensed 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 unc.lib.cdr.workbench.project; import gov.loc.mets.AmdSecType; import gov.loc.mets.DivType; import gov.loc.mets.DocumentRoot; import gov.loc.mets.FLocatType; import gov.loc.mets.FileGrpType; import gov.loc.mets.FileGrpType1; import gov.loc.mets.FileType; import gov.loc.mets.MDTYPEType; import gov.loc.mets.MdSecType; import gov.loc.mets.MdWrapType; import gov.loc.mets.MetsFactory; import gov.loc.mets.MetsPackage; import gov.loc.mets.MetsType; import gov.loc.mets.MetsType1; import gov.loc.mets.StructMapType; import gov.loc.mets.XmlDataType1; import gov.loc.mets.provider.MetsItemProviderAdapterFactory; import gov.loc.mets.util.METSConstants; import gov.loc.mets.util.METSUtils; import gov.loc.mets.util.MetsResourceFactoryImpl; import gov.loc.mods.mods.provider.MODSItemProviderAdapterFactory; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceImpl; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.BasicExtendedMetaData; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.util.EcoreUtil.Copier; import org.eclipse.emf.ecore.util.FeatureMapUtil; import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.emf.ecore.xml.type.AnyType; import org.eclipse.emf.ecore.xml.type.XMLTypeFactory; import org.eclipse.emf.edit.command.AddCommand; import org.eclipse.emf.edit.command.RemoveCommand; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3._1999.xlink.provider.XlinkItemProviderAdapterFactory; import unc.lib.cdr.workbench.rcp.Activator; /** * @author Gregory Jansen * */ public class CdrSipExportJob extends Job { @SuppressWarnings("unused") private static final Logger LOG = LoggerFactory.getLogger(CdrSipExportJob.class); private static final String SIMPLE_PROFILE_NAMESPACE_URI = "http://cdr.unc.edu/METS/profiles/Simple"; IProject p = null; String filepath = null; private ResourceSetImpl resourceSet; private BasicCommandStack commandStack; private AdapterFactoryEditingDomain editingDomain; private BasicExtendedMetaData extendedMetaData; private ComposedAdapterFactory adapterFactory; private Resource metsResource; public CdrSipExportJob(IProject p, String filepath) { super("Exporting project '" + p.getName() + "' for submission to the CDR"); this.p = p; this.filepath = filepath; adapterFactory = new ComposedAdapterFactory(); adapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory()); adapterFactory.addAdapterFactory(new MetsItemProviderAdapterFactory()); adapterFactory.addAdapterFactory(new XlinkItemProviderAdapterFactory()); adapterFactory.addAdapterFactory(new MODSItemProviderAdapterFactory()); adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory()); } /* * (non-Javadoc) * * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime. IProgressMonitor) */ @Override protected IStatus run(IProgressMonitor monitor) { if (monitor == null) { monitor = new NullProgressMonitor(); } MetsProjectNature n = null; try { n = (MetsProjectNature) p.getNature(MetsProjectNature.NATURE_ID); } catch (CoreException e) { return new Status(Status.ERROR, Activator.PLUGIN_ID, "There was a problem obtaining the METS project nature.", e); } MetsType1 workbench = n.getMets(); // initialize a new EMF environment resourceSet = new ResourceSetImpl(); // Register the appropriate resource factory to handle all file // extensions. resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap() .put(Resource.Factory.Registry.DEFAULT_EXTENSION, new MetsResourceFactoryImpl()); // Register the package to ensure it is available during loading. resourceSet.getPackageRegistry().put(MetsPackage.eNS_URI, MetsPackage.eINSTANCE); // Create the command stack that will notify this editor as commands are // executed. commandStack = new BasicCommandStack(); editingDomain = new AdapterFactoryEditingDomain(adapterFactory, commandStack, resourceSet); extendedMetaData = new BasicExtendedMetaData(resourceSet.getPackageRegistry()); // create export destination URI File f = new File(this.filepath); if(f.exists()) { f.delete(); } String uri = f.toURI().toString(); metsResource = this.resourceSet.createResource(URI.createURI(uri)); ((ResourceImpl) this.metsResource).setIntrinsicIDToEObjectMap(new HashMap()); EcoreUtil.Copier copier = new Copier(); MetsType1 cdr = (MetsType1) copier.copy(workbench); copier.copyReferences(); DocumentRoot root = MetsFactory.eINSTANCE.createDocumentRoot(); root.setMets(cdr); metsResource.getContents().add(root); // remove workbench cruft removeUnlinkedOrUnsupportedMdSecTypes(cdr); removeExtraLinkedMdSecTypes(cdr); removeNonStageFLocat(cdr); removeNonObjectsFileGroups(cdr); removeEmptyStructLink(cdr); removeEmptyFileTypes(cdr); removeEmptyAmdSecTypes(cdr); recordDepositAdmSec(cdr); // moveObjectsFileGroupsToFileSec(cdr); // cleanup PROFILE and TYPEs.. cdr.setPROFILE(SIMPLE_PROFILE_NAMESPACE_URI); StructMapType map = (StructMapType) METSUtils.findBagDiv(cdr).eContainer(); map.eUnset(MetsPackage.eINSTANCE.getStructMapType_TYPE()); try { Map<String, String> xmlOptions = new HashMap<String, String>(); xmlOptions.put(XMLResource.OPTION_ENCODING, "utf-8"); this.metsResource.save(xmlOptions); LOG.debug("mets export saved to: {}", uri); } catch (IOException e1) { LOG.error("Problem creating METS", e1); Status s = new Status(Status.ERROR, Activator.PLUGIN_ID, "Cannot save METS", e1); return s; } for(IFile file : ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(f.toURI())) { try { file.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor()); } catch (CoreException e) { e.printStackTrace(); } } return Status.OK_STATUS; } private void recordDepositAdmSec(MetsType1 cdr) { // reference AMDID in METS String depositAmdSecID = "depositAmdSec"; cdr.getMetsHdr().setADMID(Collections.singletonList(depositAmdSecID)); // wrapper elements AmdSecType amd = MetsFactory.eINSTANCE.createAmdSecType(); amd.setID(depositAmdSecID); MdSecType md = MetsFactory.eINSTANCE.createMdSecType(); md.setID("depositSourceMD"); amd.getSourceMD().add(md); MdWrapType mdWrap = MetsFactory.eINSTANCE.createMdWrapType(); mdWrap.setMDTYPE(MDTYPEType.OTHER); md.setMdWrap(mdWrap); XmlDataType1 xml = MetsFactory.eINSTANCE.createXmlDataType1(); mdWrap.setXmlData(xml); // write properties container element EStructuralFeature propertiesNodeFeature = extendedMetaData.demandFeature(SIMPLE_PROFILE_NAMESPACE_URI, "properties", true); AnyType rootTreeNode = XMLTypeFactory.eINSTANCE.createAnyType(); xml.getAny().add(propertiesNodeFeature, rootTreeNode); // write staging location element java.net.URI stagingURI = MetsProjectNature.get(p).getStagingManifestURI(); EStructuralFeature stagingLocationNodeFeature = extendedMetaData.demandFeature(SIMPLE_PROFILE_NAMESPACE_URI, "stagingLocation", true); AnyType slTreeNode = XMLTypeFactory.eINSTANCE.createAnyType(); rootTreeNode.getAny().add(stagingLocationNodeFeature, slTreeNode); FeatureMapUtil.addText(slTreeNode.getMixed(), stagingURI.toString()); Command add = AddCommand.create(editingDomain, cdr, MetsPackage.eINSTANCE.getMetsType_AmdSec(), amd); if(add.canExecute()) add.execute(); } private void removeUnlinkedOrUnsupportedMdSecTypes(MetsType1 m) { final Set<MdSecType> remove = new HashSet<MdSecType>(); MdSecListWork removeUnlinked = new MdSecListWork() { @Override public void run(EList<MdSecType> list) { for(MdSecType md : list) { if (METSConstants.MD_STATUS_CROSSWALK_NOT_LINKED.equals(md.getSTATUS())) { remove.add(md); } } } }; for(AmdSecType amd : m.getAmdSec()) { // these sections not currently supported by CDR submission profile if(amd.getDigiprovMD() != null) remove.addAll(amd.getDigiprovMD()); if(amd.getSourceMD() != null) remove.addAll(amd.getSourceMD()); if(amd.getTechMD() != null) remove.addAll(amd.getTechMD()); removeUnlinked.run(amd.getRightsMD()); } removeUnlinked.run(m.getDmdSec()); Command c = RemoveCommand.create(editingDomain, remove); if (c.canExecute()) { c.execute(); } } private void removeEmptyAmdSecTypes(MetsType1 cdr) { Set<AmdSecType> del = new HashSet<AmdSecType>(); for(AmdSecType a : cdr.getAmdSec()) { if(a.eContents() == null || a.eContents().isEmpty()) { del.add(a); } } if(del.size() > 0) { Command c = RemoveCommand.create(editingDomain, del); if (c.canExecute()) { c.execute(); } } } /** * @param cdr */ private void removeEmptyStructLink(MetsType1 cdr) { if(cdr.getStructLink() != null && cdr.getStructLink().eContents().size() == 0 ) { Command c = RemoveCommand.create(editingDomain, cdr.getStructLink()); if (c.canExecute()) { c.execute(); } } } /** * @param cdr */ private void moveObjectsFileGroupsToFileSec(MetsType1 m) { List<FileGrpType1> move = new ArrayList<FileGrpType1>(); for (FileGrpType1 g : m.getFileSec().getFileGrp()) { if (METSConstants.FILEGROUP_Objects.equals(g.getID())) { for (FileGrpType f : g.getFileGrp()) { move.add((FileGrpType1) f); } } } Command remove = RemoveCommand.create(editingDomain, move); Command add = AddCommand.create(editingDomain, m.getFileSec(), MetsPackage.eINSTANCE.getFileSecType_FileGrp(), move); if (remove.canExecute()) { remove.execute(); } else { LOG.error("Cannot execute fileGrp remove command"); } if (add.canExecute()) { add.execute(); } else { LOG.error("Cannot execute fileGrp add command"); } } /** * @param m */ private void removeNonObjectsFileGroups(MetsType1 m) { Set<FileGrpType> remove = new HashSet<FileGrpType>(); for (FileGrpType g : m.getFileSec().getFileGrp()) { if (!METSConstants.FILEGROUP_Objects.equals(g.getID())) { remove.add(g); } } Command c = RemoveCommand.create(editingDomain, remove); if (c.canExecute()) { c.execute(); } } /** * @param cdr */ private void removeNonStageFLocat(MetsType m) { Set<FLocatType> remove = new HashSet<FLocatType>(); Iterator<EObject> i = m.getFileSec().eAllContents(); while (i.hasNext()) { EObject next = i.next(); if (next instanceof FLocatType) { FLocatType l = (FLocatType) next; if (METSConstants.FLocat_USE_STAGE.equals(l.getUSE())) { // l.setLOCTYPE(value) } else if (METSConstants.FLocat_USE_ORIGINAL.equals(l.getUSE())) { remove.add(l); } } } Command c = RemoveCommand.create(editingDomain, remove); if (c.canExecute()) { c.execute(); } } private void removeEmptyFileTypes(MetsType m) { Set<FileType> remove = new HashSet<FileType>(); Iterator<EObject> i = m.getFileSec().eAllContents(); while (i.hasNext()) { EObject next = i.next(); if (next instanceof FileType) { FileType l = (FileType) next; if (l.getFLocat() == null || l.getFLocat().size() == 0) { remove.add(l); } } } Command c = RemoveCommand.create(editingDomain, remove); if (c.canExecute()) { c.execute(); } } interface MdSecListWork { void run(EList<MdSecType> list); }; /** * @param cdr */ private void removeExtraLinkedMdSecTypes(MetsType m) { final HashSet<MdSecType> remove = new HashSet<MdSecType>(); Iterator<EObject> bagChildren = METSUtils.findBagDiv(m).eAllContents(); while (bagChildren.hasNext()) { EObject eo = bagChildren.next(); if (eo instanceof DivType) { DivType d = (DivType) eo; MdSecListWork prune = new MdSecListWork() { @Override public void run(EList<MdSecType> list) { if (list.size() > 1) { MdSecType userCreated = null; Set<MdSecType> others = new HashSet<MdSecType>(); for (MdSecType md : list) { if (METSConstants.MD_STATUS_USER_EDITED.equals(md.getSTATUS())) { if (userCreated != null) { others.add(md); LOG.error("found more than one user created mdSec for the same div: {}", list); } else { userCreated = md; } } else { others.add(md); } } // if userCreate not null, use it. otherwise combine the others List<MdSecType> mdSecs = new ArrayList<MdSecType>(); if (userCreated != null) { remove.addAll(others); mdSecs.add(userCreated); } else { // FIXME merge all crosswalks together into one dmdSec Iterator<MdSecType> i = others.iterator(); mdSecs.add(i.next()); while (i.hasNext()) { remove.add(i.next()); } } list.clear(); list.addAll(mdSecs); } } }; prune.run(d.getDmdSec()); prune.run(d.getMdSec()); } } Command c = RemoveCommand.create(editingDomain, remove); if (c.canExecute()) { c.execute(); } } }