org.nuxeo.dam.object.relations.AssetRelationsBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.dam.object.relations.AssetRelationsBuilder.java

Source

/*
 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     thibaud
 */
package org.nuxeo.dam.object.relations;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.directory.api.DirectoryService;
import org.nuxeo.runtime.api.Framework;

/**
 * Making some hard coded assumptions here. For example:
 * <ul>
 * <li>We have an "IPcontractRoot" whose title is "02. Licenses" (this is defined in the Studio Project)</li>
 * <li>We have only one domain and only one IPcontractRoot folder</li>
 * <li>...</li>
 * </ul>
 * 
 * @since 7.3
 */
public class AssetRelationsBuilder {

    private static final Log log = LogFactory.getLog(AssetRelationsBuilder.class);

    // The space between the name, the lowercase, there is a final dot: All this is normal.
    // We check if the file is a Composition. In the naming convention, a composition has a name that ends with
    // " Comp" or "Cmp". Then comes the file extension. So, we search for " Comp." or " cmp."
    public static final String[] COMPOSITION_VALUES = { " comp.", " cmp." };

    public static enum ASSET_TYPE {
        COMPOSITION, COMPOSITION_RESOURCE, OTHER
    };

    // As defined in the Structure Template of the Studio project
    public static final String IPCONTRACTROOT_TITLE = "02. Licenses";

    // As defined in the Structure Template of the Studio project
    public static final String ARTFILENUMBERCONTAINER_TITLE = "02. Art File Numbers";

    // As defined in the Structure Template of the Studio project
    public static final String STYLEUMBERCONTAINER_TITLE = "01. Style Numbers";

    // As defined in the "asset_nature" vocabulary
    public static final String VOC_COMPOSITION_RESOURCE = "Comp Resource";

    // As defined in the "asset_nature" vocabulary
    public static final String VOC_COMPOSITION = "Comp";

    // As defined in the "asset_nature" vocabulary
    public static final String VOC_LICENSED_ART_RESOURCE = "Licensed Art Resource";

    // As defined in the "LicenseStatus" vocabulary
    public static final String VOC_LICENSED = "Licensed";

    // As defined in the "LicenseStatus" vocabulary
    public static final String VOC_NONLICENSED = "Non-Licensed";

    public static final String VOC_DEPARTMENT = "Department";

    protected static String ipContractRootPath = null;

    protected static String artFileNumbeContainerPath = null;

    protected static String styleNumbeContainerPath = null;

    public static final String USUAL_NXQL_LAST_FILTER = " AND ecm:isCheckedInVersion = 0 AND ecm:isProxy=0 AND ecm:currentLifeCycleState != 'deleted'";

    DocumentModel doc;

    String title = null;

    // The title minus the file extension and the Composition Suffix
    String titleNoExtension = null;

    int licenseYear = 0;

    String licenseCode = "";

    String seqNumberStr = "";

    String seqNumberSuffix = "";

    String name = "";

    String department;

    String extension;

    ASSET_TYPE assetType = ASSET_TYPE.OTHER;

    CoreSession session;

    boolean docModifiedAndSaved = false;

    DirectoryService directoryService;

    List<String> departmentValues = null;

    public AssetRelationsBuilder(DocumentModel inDoc, CoreSession inSession) {
        doc = inDoc;
        session = inSession;
        loadDocTitle();

        directoryService = Framework.getService(DirectoryService.class);

        org.nuxeo.ecm.directory.Session directorySession = directoryService.open(VOC_DEPARTMENT);
        departmentValues = directorySession.getProjection(new HashMap<String, Serializable>(), "id");
        directorySession.close();

    }

    // Not 100% reliable: If an Admin changes the vocabulary, we don't know that
    // God enough for a demo.
    protected void checkDepartmentValueInDirectory(String inValue) {
        inValue = inValue == null ? null : inValue.replace("/", "-");
        if (!departmentValues.contains(inValue)) {
            Map<String, Object> entry = new HashMap<String, Object>();
            entry.put("id", inValue);
            entry.put("label", inValue);
            entry.put("parent", "");
            entry.put("obsolete", 0);
            entry.put("ordering", 10000);

            org.nuxeo.ecm.directory.Session directorySession = directoryService.open(VOC_DEPARTMENT);
            directorySession.createEntry(entry);
            directorySession.close();

            departmentValues.add(inValue);
        }
    }

    protected String getIpContractRootPath() {

        if (ipContractRootPath == null) {
            String nxql = "SELECT * FROM IPcontractRoot WHERE dc:title = '" + IPCONTRACTROOT_TITLE + "'";
            nxql += USUAL_NXQL_LAST_FILTER;

            DocumentModelList docs = session.query(nxql);
            // We give up if we don't find it
            if (docs.size() == 0) {
                throw new RuntimeException("Cannot find a IPcontractRoot with dc:title of " + IPCONTRACTROOT_TITLE);
            }
            ipContractRootPath = docs.get(0).getPathAsString();
        }

        return ipContractRootPath;
    }

    protected String getArtFileNumberContainerPath() {

        if (artFileNumbeContainerPath == null) {
            String nxql = "SELECT * FROM ArtFileNumberContainer WHERE dc:title = '" + ARTFILENUMBERCONTAINER_TITLE
                    + "'";
            nxql += USUAL_NXQL_LAST_FILTER;

            DocumentModelList docs = session.query(nxql);
            // We give up if we don't find it
            if (docs.size() == 0) {
                throw new RuntimeException(
                        "Cannot find a ArtFileNumberContainer with dc:title of " + ARTFILENUMBERCONTAINER_TITLE);
            }
            artFileNumbeContainerPath = docs.get(0).getPathAsString();
        }

        return artFileNumbeContainerPath;
    }

    protected String getStyleNumberContainerPath() {

        return getStyleNumberContainerPath(session);
    }

    public static String getStyleNumberContainerPath(CoreSession inSession) {

        if (styleNumbeContainerPath == null) {
            String nxql = "SELECT * FROM StyleNumberContainer WHERE dc:title = '" + STYLEUMBERCONTAINER_TITLE + "'";
            nxql += USUAL_NXQL_LAST_FILTER;

            DocumentModelList docs = inSession.query(nxql);
            // We give up if we don't find it
            if (docs.size() == 0) {
                throw new RuntimeException(
                        "Cannot find a StyleNumberContainer with dc:title of " + STYLEUMBERCONTAINER_TITLE);
            }
            styleNumbeContainerPath = docs.get(0).getPathAsString();
        }

        return styleNumbeContainerPath;
    }

    /*
     * Just a way to centralize code, the day we read the title in another place than dc:title. 1st implementation:
     * Assumes the title equals the filename of the binary
     */
    protected void loadDocTitle() {

        if (doc == null) {
            title = null;
        } else {
            title = (String) doc.getPropertyValue("dc:title");
        }
    }

    public DocumentModel run() {

        if (!doc.getType().equals("Picture")) {
            return doc;
        }

        // Minimum 2 numbers, 4 license code, a dot and a file extension
        if (title.length() < 8) {
            return doc;
        }

        extension = "";
        int pos = title.lastIndexOf('.');
        if (pos > 0) {
            extension = title.substring(pos + 1);
            titleNoExtension = title.substring(0, pos);
        }

        // Check if starts with 2 digits
        int year = 0;
        try {
            year = Integer.parseInt(titleNoExtension.substring(0, 2));
        } catch (Exception e) {
            // Ignore, we just want to check if it starts with 2 numbers
        }

        // Starts with 2 digits and ends with the correct suffix => it's a composition
        boolean foundComp = false;
        String titleLC = title.toLowerCase();
        for (String compValue : COMPOSITION_VALUES) {
            pos = titleLC.indexOf(compValue);
            if (pos > 0) { // sure does not start with the value.
                foundComp = true;

                titleNoExtension = titleNoExtension.substring(0, pos);
                if (year > 0) {
                    doc = handleLicensedCompositionAsset();

                } else {
                    doc = handleNonLicensedCompositionResource();
                }

                break;
            }
        }

        // Not a composition but still maybe an interesting final resource, to link to a License
        if (!foundComp) {
            if (year > 0) {
                doc = handleLicensedNonCompositionAsset();
            } else {
                doc = HandleNonLicensedAsset();
            }
        }

        return doc;
    }

    /*
     * At this step, we are sure the title is ok, starts with 2 digits, etc. IMPORTANT: We don't handle a start with "0"
     */
    // 15BTMN002 King Of Bats cmp.psd
    // 15BTMN002B King Of Bats cmp.psd
    protected DocumentModel handleLicensedCompositionAsset() {

        return autoLinkLicensedAsset(ASSET_TYPE.COMPOSITION, VOC_COMPOSITION);
    }

    // example: GR125 basic crew with side tie COMP.psd
    // or JR228 Gym Tote Comp.psd
    // GR is the syle, 228 (or 125) the sequence number, then we have the name
    protected DocumentModel handleNonLicensedCompositionResource() {

        String tmp;
        int pos;

        initValues();

        // -------------------- Extract Info --------------------
        assetType = ASSET_TYPE.COMPOSITION_RESOURCE;

        tmp = titleNoExtension;
        pos = tmp.toLowerCase().lastIndexOf(" comp");
        if (pos > 0) {
            titleNoExtension = tmp.substring(0, pos);
        }

        tmp = titleNoExtension;
        pos = tmp.indexOf(" ");
        if (pos > 0) {
            name = tmp.substring(pos + 1);
            tmp = tmp.substring(0, pos);

            String seqNum = "";
            // First 1-n letters are the department
            char[] chars = new char[tmp.length()];
            tmp.getChars(0, tmp.length(), chars, 0);
            int i = 0;
            for (char c : chars) {
                if (Character.isDigit(c)) {
                    seqNum = tmp.substring(i);
                    break;
                } else {
                    department += Character.toString(c);
                }
                i += 1;
            }

            SeqNumberExtractor sne = new SeqNumberExtractor(seqNum);
            seqNumberStr = sne.numberAsStr;
            seqNumberSuffix = sne.suffix;
        }

        // -------------------- Check --------------------
        // If we don't have enough information, we just do nothing
        if (StringUtils.isBlank(department) || StringUtils.isBlank(seqNumberStr) || StringUtils.isBlank(name)) {
            return doc;
        }

        // -------------------- Link to the StyleNumber --------------------
        checkDepartmentValueInDirectory(department);
        DocumentModel styleDoc;
        // A StyleNumber document has the "linking" and the "style_number" schemas (among others)
        String nxql = "SELECT * FROM StyleNumber WHERE style_number:department = '" + department + "'";
        nxql += " AND style_number:number = '" + seqNumberStr + "'";
        nxql += " AND style_number:short_name = '" + name + "'";
        nxql += USUAL_NXQL_LAST_FILTER;
        DocumentModelList docs = session.query(nxql);
        if (docs.size() == 0) {
            styleDoc = session.createDocumentModel(getStyleNumberContainerPath(), department + seqNumberStr + name,
                    "StyleNumber");

            styleDoc.setPropertyValue("style_number:department", department);
            styleDoc.setPropertyValue("style_number:number", seqNumberStr);
            styleDoc.setPropertyValue("style_number:short_name", name);

            styleDoc = session.createDocument(styleDoc);
            styleDoc = session.saveDocument(styleDoc);

        } else {
            styleDoc = docs.get(0);
        }
        doc.setPropertyValue("linking:style_number_id", styleDoc.getId());

        // -------------------- Last Update(s) --------------------
        doc.setPropertyValue("asset:nature", VOC_COMPOSITION_RESOURCE);
        doc.setPropertyValue("asset:variation_letter", seqNumberSuffix);
        // doc.setPropertyValue("asset:licensing", VOC_LICENSED);

        // -------------------- Ok, we're done --------------------
        doc = session.saveDocument(doc);
        docModifiedAndSaved = true;
        return doc;
    }

    protected DocumentModel handleLicensedNonCompositionAsset() {

        return autoLinkLicensedAsset(ASSET_TYPE.OTHER, VOC_LICENSED_ART_RESOURCE);

    }

    protected DocumentModel HandleNonLicensedAsset() {

        initValues();

        return doc;
    }

    protected DocumentModel autoLinkLicensedAsset(ASSET_TYPE inType, String inNature) {
        String tmp;
        int pos;

        initValues();

        // -------------------- Extract Info --------------------
        assetType = inType;

        licenseYear = Integer.parseInt(titleNoExtension.substring(0, 2));
        licenseCode = title.substring(2, 6);

        // License sequence number is as long the character is a digit
        tmp = titleNoExtension.substring(6);
        // Find the space after the seq. number
        pos = tmp.indexOf(" ");
        if (pos > 0) {

            name = tmp.substring(pos + 1);
            String seqNum = tmp.substring(0, pos);

            SeqNumberExtractor sne = new SeqNumberExtractor(seqNum);
            seqNumberStr = sne.numberAsStr;
            seqNumberSuffix = sne.suffix;
        }

        // -------------------- Check --------------------
        // If we don't have enough information, we just do nothing
        if (StringUtils.isBlank(licenseCode) || StringUtils.isBlank(seqNumberStr) || StringUtils.isBlank(name)) {
            return doc;
        }

        // -------------------- Link to the License --------------------
        // (IPcontract document type in Studio project)
        DocumentModel licenseDoc;
        String licenseDocId;
        String nxql = "SELECT * FROM IPcontract WHERE license:year = " + licenseYear;
        nxql += " AND license:product_line_code = '" + licenseCode + "'";
        nxql += USUAL_NXQL_LAST_FILTER;

        DocumentModelList docs = session.query(nxql);
        if (docs.size() == 0) {
            licenseDoc = session.createDocumentModel(getIpContractRootPath(),
                    "" + licenseYear + licenseCode + " " + licenseCode, "IPcontract");

            licenseDoc.setPropertyValue("license:year", licenseYear);
            licenseDoc.setPropertyValue("license:product_line_code", licenseCode);
            // We don't have the product_line when extracting from a file
            licenseDoc.setPropertyValue("license:product_line", licenseCode);
            licenseDoc.setPropertyValue("license:has_default_product_line", true);

            licenseDoc = session.createDocument(licenseDoc);
            licenseDoc = session.saveDocument(licenseDoc);
        } else {
            licenseDoc = docs.get(0);
        }
        licenseDocId = licenseDoc.getId();
        doc.setPropertyValue("linking:license_id", licenseDocId);

        // -------------------- Link to the ArtFileNumber --------------------
        DocumentModel afnDoc;
        // An ArtFileNumber document has the "linking" and the "ArtFileNumber" schemas (among others)
        nxql = "SELECT * FROM ArtFileNumber WHERE linking:license_id = '" + licenseDocId + "'";
        nxql += " AND art_file_number:number = '" + seqNumberStr + "'";
        nxql += USUAL_NXQL_LAST_FILTER;
        docs = session.query(nxql);
        if (docs.size() == 0) {
            afnDoc = session.createDocumentModel(getArtFileNumberContainerPath(), seqNumberStr + name,
                    "ArtFileNumber");

            afnDoc.setPropertyValue("linking:license_id", licenseDocId);
            afnDoc.setPropertyValue("art_file_number:number", seqNumberStr);
            afnDoc.setPropertyValue("art_file_number:short_name", name);

            afnDoc = session.createDocument(afnDoc);
            afnDoc = session.saveDocument(afnDoc);

        } else {
            afnDoc = docs.get(0);
        }
        doc.setPropertyValue("linking:art_file_number_id", afnDoc.getId());

        // -------------------- Last Update(s) --------------------
        doc.setPropertyValue("asset:nature", inNature);
        doc.setPropertyValue("asset:variation_letter", seqNumberSuffix);
        doc.setPropertyValue("asset:licensing", VOC_LICENSED);

        // -------------------- Ok, we're done --------------------
        doc = session.saveDocument(doc);
        docModifiedAndSaved = true;

        return doc;
    }

    protected void initValues() {

        licenseYear = 0;
        licenseCode = "";
        seqNumberStr = "";
        seqNumberSuffix = "";
        name = "";
        department = "";

        assetType = ASSET_TYPE.OTHER;
    }

    private class SeqNumberExtractor {
        private String numberAsStr = "";

        private String suffix = "";

        private SeqNumberExtractor(String inSeqNum) {

            if (StringUtils.isBlank(inSeqNum)) {
                return;
            }

            char[] chars = new char[inSeqNum.length()];
            inSeqNum.getChars(0, inSeqNum.length(), chars, 0);
            boolean isSuffix = false;
            for (char c : chars) {
                if (isSuffix) {
                    suffix += Character.toString(c);
                } else {
                    if (Character.isDigit(c)) {
                        numberAsStr += Character.toString(c);
                    } else {
                        isSuffix = true;
                        suffix += Character.toString(c);
                    }
                }
            }
        }

    }

    public int getLicenseYear() {
        return licenseYear;
    }

    public String getLicenseCode() {
        return licenseCode;
    }

    public String getSeqNumberStr() {
        return seqNumberStr;
    }

    public String getSeqNumberSuffix() {
        return seqNumberSuffix;
    }

    public String getName() {
        return name;
    }

    public ASSET_TYPE getAssetType() {
        return assetType;
    }

    public boolean docWasModifiedAndSaved() {
        return docModifiedAndSaved;
    }

    public String getDepartment() {
        return department;
    }

    public String toJsonString() {

        String str = "";

        str = "{";
        str += "\"licenseYear\":" + licenseYear + ",";
        str += "\"licenseCode\":\"" + licenseCode + "\",";
        str += "\"seqNumberStr\":\"" + seqNumberStr + "\",";
        str += "\"seqNumberSuffix\":\"" + seqNumberSuffix + "\",";
        str += "\"assetType\":\"" + assetType.toString() + "\"";
        str += "}";

        return str;
    }
}