/**
* Copyright (C) 2001-2005 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.objectweb.speedo.mapper.lib;
import org.objectweb.jorm.api.PMapper;
import org.objectweb.speedo.api.SpeedoProperties;
import org.objectweb.speedo.mapper.api.ClassPropertyManager;
import org.objectweb.speedo.mim.api.HomeItf;
import org.objectweb.speedo.tools.StringReplace;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;
public class ClassPropertiesManager implements SpeedoProperties {
public final static char FIELD_SEP = '#';
final Properties speedoProperties;
final HashMap name2prop;
final HashMap pattern2prop;
/**
* key = property prefix
* value = ClassProperty instance
*/
final HashMap prefix2Impl;
/**
* The mapper holding homes of the persistent classes
*/
protected PMapper mapper = null;
protected Logger logger;
public ClassPropertiesManager() {
speedoProperties = new Properties();
name2prop = new HashMap();
pattern2prop = new HashMap();
prefix2Impl = new HashMap();
addClassPropertyManager(new CPMCacheClassPolicy());
addClassPropertyManager(new CPMShareablePolicy());
addClassPropertyManager(new CPMTransactionLockingLevel());
addClassPropertyManager(new CPMUserCachePolicy());
addClassPropertyManager(new CPMPrefetchOnQuery());
addClassPropertyManager(new CPMPrefetchOnExtent());
addClassPropertyManager(new CPMPrefetchOnGenClass());
}
public void addClassPropertyManager(ClassPropertyManager cpm) {
prefix2Impl.put(cpm.getName(), cpm);
}
/**
* Sets all properties.
* @param props
*/
public void addProperties(Properties props) {
if (props == null || props.size() == 0) {
return;
}
String[] classNames = mapper.getMappedClasses();
boolean apply = classNames != null && classNames.length > 0;
for(Iterator it=props.entrySet().iterator(); it.hasNext();) {
Map.Entry me = (Map.Entry) it.next();
Prop prop = createProperty(
(String) me.getKey(), (String) me.getValue());
if (apply) { // && prop.patternIsRegExp
applyProperty(prop, classNames);
}
}
}
/**
* Set a property
* @param name is the property name
* @param value is the property value
*/
public void addProperty(String name, String value) {
if (value == null || value.length() == 0) {
removeProperty(name);
} else {
Prop prop = createProperty(name, value);
if (prop.patternIsRegExp) {
String[] classNames = mapper.getMappedClasses();
if (classNames != null && classNames.length > 0) {
applyProperty(prop, classNames);
}
}
}
}
/**
* Applies current properties to a home of a persistent class.
* @param sh is the home of the persistent class.
*/
public void applyProperties(HomeItf sh) {
if (pattern2prop.size()==0) {
for (Iterator it = prefix2Impl.values().iterator(); it.hasNext();) {
ClassPropertyManager cpm = (ClassPropertyManager) it.next();
cpm.applyDefault(sh, speedoProperties, logger);
}
return;
}
String path = sh.getPath();
for (Iterator iter = pattern2prop.values().iterator(); iter.hasNext();) {
Prop prop = (Prop) iter.next();
if (prop.match(path)) {
prop.applyProperty(sh);
}
}
}
/**
* Applies current properties to a home of a generic persistent class.
* @param gcHome is the home of the persistent class.
*/
public void applyProperties(HomeItf gcHome, HomeItf refHome) {
if (pattern2prop.size()==0) {
for (Iterator it = prefix2Impl.values().iterator(); it.hasNext();) {
ClassPropertyManager cpm = (ClassPropertyManager) it.next();
cpm.applyDefault(gcHome, refHome, speedoProperties, logger);
}
return;
}
String path = gcHome.getPath();
for (Iterator iter = pattern2prop.values().iterator(); iter.hasNext();) {
Prop prop = (Prop) iter.next();
if (prop.match(path)) {
prop.applyProperty(gcHome, refHome);
}
}
}
public Properties getProperties() {
return speedoProperties;
}
private synchronized Prop createProperty(String name, String value) {
if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "Set property " + name + "=" + value);
}
return new Prop(name, value);
}
private synchronized void removeProperty(String name) {
if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "Remove property " + name);
}
speedoProperties.remove(name);
Prop oldProp = (Prop) name2prop.remove(name);
if (oldProp != null && oldProp.pa != null) {
pattern2prop.remove(oldProp.pa);
}
}
/**
* Internal representation of a property.
* A property has a pattern if it follows this profile of
* expression: xxx(yyy) where xxx is the prefix and yyy is the pattern
* characterizing class names.
*
* @author S.Chassande-Barrioz
*/
private final class Prop {
/**
* The name of the property
*/
public final String name;
/**
* The value of the property
*/
public final String value;
public final String prefix;
public final String pattern;
public final boolean patternOnField;
public final boolean patternIsRegExp;
public final Pattern pa;
public ClassPropertyManager cpm;
public Prop(String n, String v) {
this.name = n;
this.value = v;
speedoProperties.put(name, value);
Prop oldProp = (Prop) name2prop.put(name, this);
if (oldProp != null && oldProp.pa != null) {
pattern2prop.remove(oldProp.pa);
}
int idx1 = name.indexOf('(');
int idx2 = name.indexOf(')');
if (idx2 > idx1 && idx1 > 0) {
this.prefix = name.substring(0, idx1);
cpm = (ClassPropertyManager) prefix2Impl.get(this.prefix);
this.pattern = name.substring(idx1 + 1, idx2);
this.patternIsRegExp = this.pattern != null
&& (this.pattern.indexOf('*') != -1 || this.pattern.indexOf('?') != -1);
this.patternOnField = pattern.indexOf(FIELD_SEP) > 0;
if (this.patternIsRegExp) {
this.pa = Pattern.compile(StringReplace.toJavaPattern(this.pattern));
pattern2prop.put(pa, this);
} else {
this.pa = null;
}
} else {
this.prefix = null;
this.pattern = null;
this.patternOnField = false;
this.patternIsRegExp = false;
this.pa = null;
this.cpm = null;
if (idx1 == -1 && idx2 == -1) {
//no pattern and no prefix
} else if (idx1 >= idx2 && logger != null) {
logger.log(BasicLevel.WARN, "Property '" + name + "' is malformed");
}
}
}
public boolean match(String path) {
boolean match = patternIsRegExp && pa.matcher(path).matches();
if (match && logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "Property '" + name
+ "' matches the pattern '" + path
+ "', value=" + value);
}
return match;
}
public void applyProperty(HomeItf sh) {
if (cpm == null) {
cpm = (ClassPropertyManager) prefix2Impl.get(prefix);
}
if (cpm == null) {
logger.log(BasicLevel.WARN, "Cannot apply the property '"
+ name + "' to '" + sh.getPath() + "': "
+ "\n\tprefix= " + prefix
+ "\nprefix2Impl=" + prefix2Impl);
} else {
cpm.applyProperty(name, value, sh, speedoProperties, logger);
}
}
public void applyProperty(HomeItf gcHome, HomeItf refHome) {
if (cpm == null) {
cpm = (ClassPropertyManager) prefix2Impl.get(prefix);
}
if (cpm == null) {
logger.log(BasicLevel.WARN, "Cannot apply the property '"
+ name + "' to '" + gcHome.getPath() + "'.");
} else {
cpm.applyProperty(name, value, gcHome, refHome, speedoProperties, logger);
}
}
}
/**
* Applies a property on known persistent classes
* @param prop is the property to apply
* @param classNames is the list of known persistent classes
*/
private void applyProperty(final Prop prop, final String[] classNames) {
if (prop.pattern != null) {
return;
}
//Iterator over each konwn class to apply property if the pattern match
for (int i = 0; i < classNames.length; i++) {
if (prop.patternOnField) {
//Fetch list of fields belong the class
HomeItf sh = (HomeItf) mapper.lookup(classNames[i]);
//TODO: provide in HomeItf (PClassMapping maybe) a new method
// returning the list of persistent field.
String[] fieldNames = new String[0]; //sh.get ...
for (int j = 0; j < fieldNames.length; j++) {
//build the path from the class name and the field name
String path = classNames[i] + FIELD_SEP + fieldNames[j];
if (prop.match(path)) {
//The path matches
HomeItf subHome;
try {
//Try to find the home of the gen clas if the field
// corresponds to a genclass reference
subHome = (HomeItf) sh.getGenClassMapping(fieldNames[i]);
} catch (UnsupportedOperationException e) {
//The field is not a reference to gen class
subHome = sh;
}
//apply the property on the home
prop.applyProperty(subHome);
}
}
} else if (prop.match(classNames[i])) {
//the property is not for a field and matches to the class name
String path = classNames[i];
//find the home corresponding to the class name
HomeItf sh = (HomeItf) mapper.lookup(path);
//apply the property on the home
prop.applyProperty(sh);
}
}
}
}