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