001/*
002 * Copyright (c) 2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Florent Guillaume
011 */
012package org.nuxeo.ecm.core.schema;
013
014import static org.apache.commons.lang.ObjectUtils.NULL;
015import static org.nuxeo.ecm.core.schema.types.ComplexTypeImpl.canonicalXPath;
016
017import java.io.Serializable;
018import java.util.HashMap;
019import java.util.LinkedList;
020import java.util.List;
021import java.util.Map;
022
023import org.nuxeo.ecm.core.schema.types.Schema;
024import org.nuxeo.runtime.api.Framework;
025
026/**
027 * Information about what's to be prefetched: individual properties and whole schemas.
028 */
029public class Prefetch implements Serializable {
030
031    private static final long serialVersionUID = 1L;
032
033    /**
034     * map of prefix:name -> value
035     * <p>
036     * key can be a canonical xpath like prefix:name/0/othername
037     * <p>
038     * null values are stored as actual nulls
039     */
040    public Map<String, Serializable> values;
041
042    /**
043     * map of schema -> list of prefix:name
044     */
045    public Map<String, List<String>> keysBySchema;
046
047    /**
048     * map of schema -> name -> prefix:name
049     */
050    public Map<String, Map<String, String>> keysBySchemaAndName;
051
052    public Prefetch() {
053        values = new HashMap<String, Serializable>();
054        keysBySchema = new HashMap<String, List<String>>();
055        keysBySchemaAndName = new HashMap<String, Map<String, String>>();
056    }
057
058    public boolean isEmpty() {
059        return values.isEmpty();
060    }
061
062    public void put(String prefixedName, String schemaName, String name, Serializable value) {
063        values.put(prefixedName, value);
064        if (schemaName != null) {
065            Map<String, String> keysByName = keysBySchemaAndName.get(schemaName);
066            if (keysByName == null) {
067                keysBySchemaAndName.put(schemaName, keysByName = new HashMap<String, String>());
068            }
069            keysByName.put(name, prefixedName);
070            List<String> keys = keysBySchema.get(schemaName);
071            if (keys == null) {
072                keysBySchema.put(schemaName, keys = new LinkedList<String>());
073            }
074            keys.add(prefixedName);
075        }
076    }
077
078    public Serializable get(String xpath) {
079        xpath = canonicalXPath(xpath);
080        if (values.containsKey(xpath)) {
081            return cloned(values.get(xpath));
082        }
083        return NULL;
084    }
085
086    public Serializable get(String schemaName, String name) {
087        Map<String, String> keysByName = keysBySchemaAndName.get(schemaName);
088        if (keysByName != null) {
089            String prefixedName = keysByName.get(name);
090            if (prefixedName != null && values.containsKey(prefixedName)) {
091                return cloned(values.get(prefixedName));
092            }
093        }
094        return NULL;
095    }
096
097    // make sure we return a new array
098    protected Serializable cloned(Serializable value) {
099        if (value instanceof Object[]) {
100            value = ((Object[]) value).clone();
101        }
102        return value;
103    }
104
105    public boolean isPrefetched(String xpath) {
106        xpath = canonicalXPath(xpath);
107        return values.containsKey(xpath);
108    }
109
110    public boolean isPrefetched(String schemaName, String name) {
111        Map<String, String> keysByName = keysBySchemaAndName.get(schemaName);
112        if (keysByName == null) {
113            return false;
114        }
115        String prefixedName = keysByName.get(name);
116        if (prefixedName == null) {
117            return false;
118        }
119        return values.containsKey(prefixedName);
120    }
121
122    /**
123     * Clears the prefetches for a given schema.
124     */
125    public void clearPrefetch(String schemaName) {
126        keysBySchemaAndName.remove(schemaName);
127        List<String> keys = keysBySchema.remove(schemaName);
128        if (keys != null) {
129            for (String prefixedName : keys) {
130                values.remove(prefixedName);
131            }
132        }
133    }
134
135    /**
136     * Gets the schema name for a given xpath.
137     * <p>
138     * The type is used to resolve non-prefixed properties.
139     *
140     * @return the schema name or {@code null}
141     */
142    public String getXPathSchema(String xpath, DocumentType type) {
143        xpath = canonicalXPath(xpath);
144        int i = xpath.indexOf('/');
145        String prop = i == -1 ? xpath : xpath.substring(0, i);
146        int p = prop.indexOf(':');
147        if (p == -1) {
148            for (Schema schema : type.getSchemas()) {
149                if (schema.hasField(prop)) {
150                    return schema.getName();
151                }
152            }
153            return null;
154        } else {
155            String prefix = prop.substring(0, p);
156            SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class);
157            Schema schema = schemaManager.getSchemaFromPrefix(prefix);
158            if (schema == null) {
159                schema = schemaManager.getSchema(prefix);
160            }
161            return schema == null ? null : schema.getName();
162        }
163    }
164
165}