001/* 002 * Copyright (c) 2006-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 * Bogdan Stefanescu 011 * Florent Guillaume 012 */ 013 014package org.nuxeo.ecm.core.schema; 015 016import static org.nuxeo.ecm.core.schema.types.ComplexTypeImpl.canonicalXPath; 017 018import java.io.Serializable; 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.LinkedList; 022import java.util.List; 023import java.util.regex.Pattern; 024 025import org.apache.commons.lang.StringUtils; 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028import org.nuxeo.ecm.core.schema.types.ComplexType; 029import org.nuxeo.ecm.core.schema.types.Field; 030import org.nuxeo.ecm.core.schema.types.ListType; 031import org.nuxeo.ecm.core.schema.types.Schema; 032import org.nuxeo.ecm.core.schema.types.Type; 033import org.nuxeo.runtime.api.Framework; 034 035/** 036 * Information about what's to be prefetched: individual properties and whole schemas. 037 */ 038public class PrefetchInfo implements Serializable { 039 040 private static final long serialVersionUID = -6495547095819614741L; 041 042 private static final Log log = LogFactory.getLog(PrefetchInfo.class); 043 044 private final String expr; 045 046 private transient String[] fields; 047 048 private transient String[] schemas; 049 050 public PrefetchInfo(String expr) { 051 this.expr = expr; 052 } 053 054 public String[] getSchemas() { 055 parseExpression(); 056 return schemas; 057 } 058 059 public String[] getFields() { 060 parseExpression(); 061 return fields; 062 } 063 064 private static final Pattern STAR_OR_DIGITS = Pattern.compile("\\*|(\\d+)"); 065 066 private void parseExpression() { 067 if (fields != null || expr == null) { 068 return; 069 } 070 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 071 List<String> fields = new ArrayList<String>(); 072 List<String> schemas = new ArrayList<String>(); 073 074 for (String s : expr.split("[ \t\n\r,]")) { 075 if (s.isEmpty()) { 076 continue; 077 } 078 s = canonicalXPath(s); 079 080 // maybe a schema? 081 Schema schema = schemaManager.getSchema(s); 082 if (schema != null) { 083 schemas.add(s); 084 continue; 085 } 086 087 // isolate first field of property if complex 088 String[] props = s.split("/"); 089 if (props.length == 0) { 090 continue; 091 } 092 String prop = props[0]; 093 List<String> complex = Arrays.asList(props).subList(1, props.length); 094 095 // get the field 096 List<String> parts = new LinkedList<String>(); 097 Field field; 098 int i = prop.indexOf('.'); 099 if (i != -1) { 100 // try schemaName.fieldName 101 String schemaName = prop.substring(0, i); 102 String fieldName = prop.substring(i + 1); 103 schema = schemaManager.getSchema(schemaName); 104 field = schema == null ? null : schema.getField(fieldName); 105 } else { 106 // otherwise try prefixed name 107 field = schemaManager.getField(prop); 108 // TODO must deal with prefix-less props like "size" 109 } 110 if (field == null) { 111 logNotFound(s); 112 continue; 113 } 114 parts.add(field.getName().getPrefixedName()); 115 116 // got a field, check its complex properties 117 for (String t : complex) { 118 Type fieldType = field.getType(); 119 if (fieldType.isComplexType()) { 120 // complex type, get subfield 121 ComplexType fieldComplexType = (ComplexType) fieldType; 122 field = fieldComplexType.getField(t); 123 parts.add(t); 124 continue; 125 } else if (fieldType.isListType()) { 126 ListType listType = (ListType) fieldType; 127 if (!listType.getFieldType().isSimpleType()) { 128 // complex list 129 // should be * or an integer 130 if (!STAR_OR_DIGITS.matcher(t).matches()) { 131 field = null; 132 break; 133 } 134 field = listType.getField(); 135 parts.add("*"); 136 } else { 137 // array, cannot have subproperties 138 field = null; 139 break; 140 } 141 } else { 142 // primitive type, cannot have subproperties 143 field = null; 144 break; 145 } 146 } 147 if (field == null) { 148 logNotFound(s); 149 continue; 150 } 151 if (!isScalarField(field)) { 152 log.error("Prefetch field '" + s + "' is not scalar"); 153 continue; 154 } 155 fields.add(StringUtils.join(parts, '/')); 156 } 157 158 this.fields = fields.toArray(new String[fields.size()]); 159 this.schemas = schemas.toArray(new String[schemas.size()]); 160 } 161 162 /** 163 * Checks if a field is a primitive type or array. 164 */ 165 private static boolean isScalarField(Field field) { 166 Type fieldType = field.getType(); 167 if (fieldType.isComplexType()) { 168 // complex type 169 return false; 170 } 171 if (!fieldType.isListType()) { 172 // primitive type 173 return true; 174 } 175 // array or complex list? 176 return ((ListType) fieldType).getFieldType().isSimpleType(); 177 } 178 179 private static void logNotFound(String s) { 180 log.error("Prefetch field or schema '" + s + "' not found"); 181 } 182 183}