/*
* Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.quercus.env;
import com.caucho.quercus.Quercus;
import com.caucho.quercus.QuercusException;
import com.caucho.quercus.function.AbstractFunction;
import com.caucho.quercus.program.ClassDef;
import com.caucho.quercus.program.UnsetFunction;
import com.caucho.util.Crc64;
import com.caucho.util.L10N;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
/**
* Represents the state of the definitions: functions, classes and
* constants.
*/
public final class DefinitionState {
private static final L10N L = new L10N(DefinitionState.class);
private final Quercus _quercus;
private boolean _isStrict;
private HashMap<String, AbstractFunction> _funMap;
private HashMap<String, AbstractFunction> _lowerFunMap;
private HashMap<String, ClassDef> _classDefMap;
private HashMap<String, ClassDef> _lowerClassDefMap;
private boolean _isLazy;
// crc of the entries
private long _crc;
public DefinitionState(Quercus quercus)
{
_quercus = quercus;
_isStrict = quercus.isStrict();
_funMap = new HashMap<String, AbstractFunction>(256, 0.25F);
_classDefMap = new HashMap<String, ClassDef>(256, 0.25F);
if (! _isStrict) {
_lowerFunMap = new HashMap<String, AbstractFunction>(256, 0.25F);
_lowerClassDefMap = new HashMap<String, ClassDef>(256, 0.25F);
}
}
private DefinitionState(DefinitionState oldState)
{
this(oldState._quercus);
_funMap.putAll(oldState._funMap);
if (_lowerFunMap != null)
_lowerFunMap.putAll(oldState._lowerFunMap);
_classDefMap.putAll(oldState._classDefMap);
if (_lowerClassDefMap != null)
_lowerClassDefMap.putAll(oldState._lowerClassDefMap);
_crc = oldState._crc;
}
private DefinitionState(DefinitionState oldState, boolean isLazy)
{
_isLazy = true;
_quercus = oldState._quercus;
_isStrict = oldState._isStrict;
_funMap = oldState._funMap;
_lowerFunMap = oldState._lowerFunMap;
_classDefMap = oldState._classDefMap;
_lowerClassDefMap = oldState._lowerClassDefMap;
_crc = oldState._crc;
}
/**
* Returns true for strict mode.
*/
public final boolean isStrict()
{
return _isStrict;
}
/**
* Returns the owning PHP engine.
*/
public Quercus getQuercus()
{
return _quercus;
}
/**
* returns the crc.
*/
public long getCrc()
{
return _crc;
}
/**
* Returns an array of the defined functions.
*/
public ArrayValue getDefinedFunctions()
{
ArrayValue result = new ArrayValueImpl();
ArrayValue internal = _quercus.getDefinedFunctions();
ArrayValue user = new ArrayValueImpl();
// XXX: i18n
result.put(new StringBuilderValue("internal"), internal);
result.put(new StringBuilderValue("user"), user);
for (String name : _funMap.keySet()) {
StringValue key = new StringBuilderValue(name);
if (! internal.contains(key).isset())
user.put(name);
}
return result;
}
/**
* Finds the java reflection method for the function with the given name.
*
* @param name the method name
* @return the found method or null if no method found.
*/
public AbstractFunction findFunction(String name)
{
AbstractFunction fun = _funMap.get(name);
if (fun == null) {
}
else if (fun instanceof UnsetFunction) {
UnsetFunction unsetFun = (UnsetFunction) fun;
if (_crc == unsetFun.getCrc())
return null;
}
else {
return fun;
}
if (_lowerFunMap != null) {
fun = _lowerFunMap.get(name.toLowerCase());
if (fun != null) {
// copyOnWrite();
_funMap.put(name, fun);
return fun;
}
}
fun = findModuleFunction(name);
if (fun != null) {
// copyOnWrite();
_funMap.put(name, fun);
return fun;
}
else {
// copyOnWrite();
_funMap.put(name, new UnsetFunction(_crc));
return null;
}
}
/**
* Finds the java reflection method for the function with the given name.
*
* @param name the method name
* @return the found method or null if no method found.
*/
private AbstractFunction findModuleFunction(String name)
{
AbstractFunction fun = null;
fun = _quercus.findFunction(name);
if (fun != null)
return fun;
return fun;
}
/**
* Adds a function, e.g. from an include.
*/
public Value addFunction(String name, AbstractFunction fun)
{
AbstractFunction oldFun = findFunction(name);
if (oldFun != null) {
throw new QuercusException(L.l("can't redefine function {0}", name));
}
copyOnWrite();
_funMap.put(name, fun);
_crc = Crc64.generate(_crc, name);
if (_lowerFunMap != null)
_lowerFunMap.put(name.toLowerCase(), fun);
return BooleanValue.TRUE;
}
/**
* Adds a function from a compiled include
*
* @param name the function name, must be an intern() string
* @param lowerName the function name, must be an intern() string
*/
public Value addFunction(String name, String lowerName, AbstractFunction fun)
{
// XXX: skip the old function check since the include for compiled
// pages is already verified. Might have a switch here?
/*
AbstractFunction oldFun = _lowerFunMap.get(lowerName);
if (oldFun == null)
oldFun = _quercus.findLowerFunctionImpl(lowerName);
if (oldFun != null) {
throw new QuercusException(L.l("can't redefine function {0}", name));
}
*/
copyOnWrite();
_funMap.put(name, fun);
_crc = Crc64.generate(_crc, name);
if (_lowerFunMap != null)
_lowerFunMap.put(lowerName, fun);
return BooleanValue.TRUE;
}
/**
* Adds a class, e.g. from an include.
*/
public void addClassDef(String name, ClassDef cl)
{
copyOnWrite();
_classDefMap.put(name, cl);
_crc = Crc64.generate(_crc, name);
if (_lowerClassDefMap != null)
_lowerClassDefMap.put(name.toLowerCase(), cl);
}
/**
* Adds a class, e.g. from an include.
*/
public ClassDef findClassDef(String name)
{
ClassDef def = _classDefMap.get(name);
if (def != null)
return def;
if (_lowerClassDefMap != null)
def = _lowerClassDefMap.get(name.toLowerCase());
return def;
}
/**
* Returns the declared classes.
*
* @return an array of the declared classes()
*/
public Value getDeclaredClasses(Env env)
{
ArrayList<String> names = new ArrayList<String>();
/*
for (String name : _classMap.keySet()) {
if (! names.contains(name))
names.add(name);
}
*/
for (String name : _classDefMap.keySet()) {
if (! names.contains(name))
names.add(name);
}
for (String name : _quercus.getClassMap().keySet()) {
if (! names.contains(name))
names.add(name);
}
Collections.sort(names);
ArrayValue array = new ArrayValueImpl();
for (String name : names) {
array.put(env.createStringOld(name));
}
return array;
}
public DefinitionState copy()
{
return new DefinitionState(this);
}
public DefinitionState copyLazy()
{
return new DefinitionState(this, true);
}
private void copyOnWrite()
{
if (! _isLazy)
return;
_isLazy = false;
_funMap = new HashMap<String, AbstractFunction>(_funMap);
if (_lowerFunMap != null) {
_lowerFunMap = new HashMap<String, AbstractFunction>(_lowerFunMap);
}
_classDefMap = new HashMap<String, ClassDef>(_classDefMap);
if (_lowerClassDefMap != null) {
_lowerClassDefMap = new HashMap<String, ClassDef>(_lowerClassDefMap);
}
}
static class ClassKey {
private final WeakReference<ClassDef> _defRef;
private final WeakReference<QuercusClass> _parentRef;
ClassKey(ClassDef def, QuercusClass parent)
{
_defRef = new WeakReference<ClassDef>(def);
if (parent != null)
_parentRef = new WeakReference<QuercusClass>(parent);
else
_parentRef = null;
}
public int hashCode()
{
int hash = 37;
ClassDef def = _defRef.get();
QuercusClass parent = null;
if (_parentRef != null)
parent = _parentRef.get();
if (def != null)
hash = 65521 * hash + def.hashCode();
if (parent != null)
hash = 65521 * hash + parent.hashCode();
return hash;
}
public boolean equals(Object o)
{
ClassKey key = (ClassKey) o;
ClassDef aDef = _defRef.get();
ClassDef bDef = key._defRef.get();
if (aDef != bDef)
return false;
if (_parentRef == key._parentRef)
return true;
else if (_parentRef != null && key._parentRef != null)
return _parentRef.get() == key._parentRef.get();
else
return false;
}
}
}