Fwd: [jbosstools-commits] JBoss Tools SVN: r5415 - in trunk/core/plugins/org.jboss.ide.eclipse.archives.core: src/eclipse/org/jboss/ide/eclipse/archives/core and 5 other directories.
by Max Rydahl Andersen
Rob - why are we doing big feature changes in 2.0.1 code stream ?
...and why clone public API like StringSubstitution ?
-max
------- Forwarded message -------
From: jbosstools-commits(a)lists.jboss.org
To: jbosstools-commits(a)lists.jboss.org
Cc:
Subject: [jbosstools-commits] JBoss Tools SVN: r5415 - in trunk/core/plugins/org.jboss.ide.eclipse.archives.core: src/eclipse/org/jboss/ide/eclipse/archives/core and 5 other directories.
Date: Thu, 20 Dec 2007 21:21:48 +0100
Author: rob.stryker(a)jboss.com
Date: 2007-12-20 15:21:47 -0500 (Thu, 20 Dec 2007)
New Revision: 5415
Added:
trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/xpl/
trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/xpl/StringSubstitutionEngineClone.java
Modified:
trunk/core/plugins/org.jboss.ide.eclipse.archives.core/META-INF/MANIFEST.MF
trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/eclipse/org/jboss/ide/eclipse/archives/core/ArchivesCorePlugin.java
trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/eclipse/org/jboss/ide/eclipse/archives/core/model/other/internal/WorkspaceVariables.java
trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/ArchivesCore.java
trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/model/other/IRuntimeVariables.java
trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/model/other/internal/StandaloneVariables.java
Log:
JBIDE-1406 prelim preparation
Modified: trunk/core/plugins/org.jboss.ide.eclipse.archives.core/META-INF/MANIFEST.MF
===================================================================
--- trunk/core/plugins/org.jboss.ide.eclipse.archives.core/META-INF/MANIFEST.MF 2007-12-20 19:20:25 UTC (rev 5414)
+++ trunk/core/plugins/org.jboss.ide.eclipse.archives.core/META-INF/MANIFEST.MF 2007-12-20 20:21:47 UTC (rev 5415)
@@ -11,7 +11,8 @@
org.eclipse.core.commands,
org.apache.ant,
org.eclipse.core.resources,
- org.eclipse.jdt.core
+ org.eclipse.jdt.core,
+ org.eclipse.core.variables
Eclipse-LazyStart: true
Bundle-ClassPath: lib/concurrent.jar,
lib/truezip-6.jar,
Modified: trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/eclipse/org/jboss/ide/eclipse/archives/core/ArchivesCorePlugin.java
===================================================================
--- trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/eclipse/org/jboss/ide/eclipse/archives/core/ArchivesCorePlugin.java 2007-12-20 19:20:25 UTC (rev 5414)
+++ trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/eclipse/org/jboss/ide/eclipse/archives/core/ArchivesCorePlugin.java 2007-12-20 20:21:47 UTC (rev 5415)
@@ -34,7 +34,7 @@
public class ArchivesCorePlugin extends Plugin {
// The plug-in ID
- public static final String PLUGIN_ID = "org.jboss.ide.eclipse.archives.core";
+ public static final String PLUGIN_ID = ArchivesCore.PLUGIN_ID;
// The shared instance
private static ArchivesCorePlugin plugin;
Modified: trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/eclipse/org/jboss/ide/eclipse/archives/core/model/other/internal/WorkspaceVariables.java
===================================================================
--- trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/eclipse/org/jboss/ide/eclipse/archives/core/model/other/internal/WorkspaceVariables.java 2007-12-20 19:20:25 UTC (rev 5414)
+++ trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/eclipse/org/jboss/ide/eclipse/archives/core/model/other/internal/WorkspaceVariables.java 2007-12-20 20:21:47 UTC (rev 5415)
@@ -4,8 +4,10 @@
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.variables.VariablesPlugin;
import org.jboss.ide.eclipse.archives.core.ArchivesCorePlugin;
import org.jboss.ide.eclipse.archives.core.model.other.IRuntimeVariables;
@@ -37,4 +39,17 @@
return ArchivesCorePlugin.getDefault().isDebugging()
&& "true".equalsIgnoreCase(Platform.getDebugOption(option));
}
+
+ public String getProjectName(IPath path) {
+ IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ for( int i = 0; i < projects.length; i++ )
+ if( projects[i].getLocation().equals(path))
+ return projects[i].getName();
+ return null;
+ }
+
+ public String performStringSubstitution(String expression,
+ boolean reportUndefinedVariables) throws CoreException {
+ return VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(expression, reportUndefinedVariables);
+ }
}
Modified: trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/ArchivesCore.java
===================================================================
--- trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/ArchivesCore.java 2007-12-20 19:20:25 UTC (rev 5414)
+++ trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/ArchivesCore.java 2007-12-20 20:21:47 UTC (rev 5415)
@@ -7,6 +7,7 @@
public abstract class ArchivesCore {
+ public static final String PLUGIN_ID = "org.jboss.ide.eclipse.archives.core";
private static ArchivesCore instance;
// Due to classloader restrictions we won't be able to lazy load, but that should be ok as long
// as we keep the construction of ArchivesCore subclasses to a minimum
Modified: trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/model/other/IRuntimeVariables.java
===================================================================
--- trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/model/other/IRuntimeVariables.java 2007-12-20 19:20:25 UTC (rev 5414)
+++ trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/model/other/IRuntimeVariables.java 2007-12-20 20:21:47 UTC (rev 5415)
@@ -2,6 +2,7 @@
import java.net.URL;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
public interface IRuntimeVariables {
@@ -13,8 +14,11 @@
*/
public boolean isDebugging(String option);
public IPath getProjectPath(String projectName);
-
-// public IPath getWorkspacePath();
+ public String getProjectName(IPath path);
public URL getBindingSchema();
public URL getBindingLog4j();
+
+ // allow for variable replacement
+ public String performStringSubstitution(String expression, boolean reportUndefinedVariables) throws CoreException;
+
}
Modified: trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/model/other/internal/StandaloneVariables.java
===================================================================
--- trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/model/other/internal/StandaloneVariables.java 2007-12-20 19:20:25 UTC (rev 5414)
+++ trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/model/other/internal/StandaloneVariables.java 2007-12-20 20:21:47 UTC (rev 5415)
@@ -1,10 +1,14 @@
package org.jboss.ide.eclipse.archives.core.model.other.internal;
import java.net.URL;
+import java.util.Iterator;
+import java.util.Properties;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.jboss.ide.eclipse.archives.core.model.other.IRuntimeVariables;
+import org.jboss.ide.eclipse.archives.core.xpl.StringSubstitutionEngineClone;
public class StandaloneVariables implements IRuntimeVariables {
@@ -27,8 +31,27 @@
}
public boolean isDebugging(String option) {
- // TODO Auto-generated method stub
- return false;
+ return System.getProperty("archives.debug", "true")
+ .equals("true");
}
+ public String getProjectName(IPath path) {
+ Properties props = System.getProperties();
+ Object key, val;
+ for( Iterator i = props.keySet().iterator(); i.hasNext(); ) {
+ key = i.next();
+ if( key instanceof String && ((String)key).endsWith(".dir")) {
+ val = props.get(key);
+ if( path.toOSString().equals(new Path((String)val).toOSString()))
+ return (String)key;
+ }
+ }
+ return null;
+ }
+
+ public String performStringSubstitution(String expression,
+ boolean reportUndefinedVariables) throws CoreException {
+ return new StringSubstitutionEngineClone().performStringSubstitution(expression, reportUndefinedVariables);
+ }
+
}
Added: trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/xpl/StringSubstitutionEngineClone.java
===================================================================
--- trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/xpl/StringSubstitutionEngineClone.java (rev 0)
+++ trunk/core/plugins/org.jboss.ide.eclipse.archives.core/src/main/org/jboss/ide/eclipse/archives/core/xpl/StringSubstitutionEngineClone.java 2007-12-20 20:21:47 UTC (rev 5415)
@@ -0,0 +1,289 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.jboss.ide.eclipse.archives.core.xpl;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.jboss.ide.eclipse.archives.core.ArchivesCore;
+
+/**
+ * Performs string substitution for context and value variables.
+ */
+public class StringSubstitutionEngineClone {
+
+ // delimiters
+ private static final String VARIABLE_START = "${"; //$NON-NLS-1$
+ private static final char VARIABLE_END = '}';
+ private static final char VARIABLE_ARG = ':';
+ // parsing states
+ private static final int SCAN_FOR_START = 0;
+ private static final int SCAN_FOR_END = 1;
+
+ /**
+ * Resulting string
+ */
+ private StringBuffer fResult;
+
+ /**
+ * Whether substitutions were performed
+ */
+ private boolean fSubs;
+
+ /**
+ * Stack of variables to resolve
+ */
+ private Stack fStack;
+
+ class VariableReference {
+
+ // the text inside the variable reference
+ private StringBuffer fText;
+
+ public VariableReference() {
+ fText = new StringBuffer();
+ }
+
+ public void append(String text) {
+ fText.append(text);
+ }
+
+ public String getText() {
+ return fText.toString();
+ }
+
+ }
+
+ /**
+ * Performs recursive string substitution and returns the resulting string.
+ *
+ * @param expression expression to resolve
+ * @param reportUndefinedVariables whether to report undefined variables as an error
+ * @return the resulting string with all variables recursively
+ * substituted
+ * @exception CoreException if unable to resolve a referenced variable or if a cycle exists
+ * in referenced variables
+ */
+ public String performStringSubstitution(String expression, boolean reportUndefinedVariables ) throws CoreException {
+ substitute(expression, reportUndefinedVariables );
+ List resolvedVariableSets = new ArrayList();
+ while (fSubs) {
+ HashSet resolved = substitute(fResult.toString(), reportUndefinedVariables );
+
+ for(int i=resolvedVariableSets.size()-1; i>=0; i--) {
+
+ HashSet prevSet = (HashSet)resolvedVariableSets.get(i);
+
+ if (prevSet.equals(resolved)) {
+ HashSet conflictingSet = new HashSet();
+ for (; i<resolvedVariableSets.size(); i++)
+ conflictingSet.addAll((HashSet)resolvedVariableSets.get(i));
+
+ StringBuffer problemVariableList = new StringBuffer();
+ for (Iterator it=conflictingSet.iterator(); it.hasNext(); ) {
+ problemVariableList.append(it.next().toString());
+ problemVariableList.append(", "); //$NON-NLS-1$
+ }
+ problemVariableList.setLength(problemVariableList.length()-2); //truncate the last ", "
+ Status status = new Status(IStatus.ERROR,
+ ArchivesCore.PLUGIN_ID,
+ "Cycle found in variable replacement",
+ null);
+ throw new CoreException(status);
+ }
+ }
+
+ resolvedVariableSets.add(resolved);
+ }
+ return fResult.toString();
+ }
+
+ /**
+ * Performs recursive string validation to ensure that all of the variables
+ * contained in the expression exist
+ * @param expression expression to validate
+ * @param manager registry of variables
+ * @exception CoreException if a referenced variable does not exist or if a cycle exists
+ * in referenced variables
+ */
+ public void validateStringVariables(String expression ) throws CoreException {
+ performStringSubstitution(expression, true );
+ }
+
+ /**
+ * Makes a substitution pass of the given expression returns a Set of the variables that were resolved in this
+ * pass
+ *
+ * @param expression source expression
+ * @param reportUndefinedVariables whether to report undefined variables as an error
+ * @param resolveVariables whether to resolve the value of any variables
+ * @exception CoreException if unable to resolve a variable
+ */
+ private HashSet substitute(String expression, boolean reportUndefinedVariables) throws CoreException {
+ fResult = new StringBuffer(expression.length());
+ fStack = new Stack();
+ fSubs = false;
+
+ HashSet resolvedVariables = new HashSet();
+
+ int pos = 0;
+ int state = SCAN_FOR_START;
+ while (pos < expression.length()) {
+ switch (state) {
+ case SCAN_FOR_START:
+ int start = expression.indexOf(VARIABLE_START, pos);
+ if (start >= 0) {
+ int length = start - pos;
+ // copy non-variable text to the result
+ if (length > 0) {
+ fResult.append(expression.substring(pos, start));
+ }
+ pos = start + 2;
+ state = SCAN_FOR_END;
+
+ fStack.push(new VariableReference());
+ } else {
+ // done - no more variables
+ fResult.append(expression.substring(pos));
+ pos = expression.length();
+ }
+ break;
+ case SCAN_FOR_END:
+ // be careful of nested variables
+ start = expression.indexOf(VARIABLE_START, pos);
+ int end = expression.indexOf(VARIABLE_END, pos);
+ if (end < 0) {
+ // variables are not completed
+ VariableReference tos = (VariableReference)fStack.peek();
+ tos.append(expression.substring(pos));
+ pos = expression.length();
+ } else {
+ if (start >= 0 && start < end) {
+ // start of a nested variable
+ int length = start - pos;
+ if (length > 0) {
+ VariableReference tos = (VariableReference)fStack.peek();
+ tos.append(expression.substring(pos, start));
+ }
+ pos = start + 2;
+ fStack.push(new VariableReference());
+ } else {
+ // end of variable reference
+ VariableReference tos = (VariableReference)fStack.pop();
+ String substring = expression.substring(pos, end);
+ tos.append(substring);
+ resolvedVariables.add(substring);
+
+ pos = end + 1;
+ String value= resolve(tos, reportUndefinedVariables);
+ if (value == null) {
+ value = ""; //$NON-NLS-1$
+ }
+ if (fStack.isEmpty()) {
+ // append to result
+ fResult.append(value);
+ state = SCAN_FOR_START;
+ } else {
+ // append to previous variable
+ tos = (VariableReference)fStack.peek();
+ tos.append(value);
+ }
+ }
+ }
+ break;
+ }
+ }
+ // process incomplete variable references
+ while (!fStack.isEmpty()) {
+ VariableReference tos = (VariableReference)fStack.pop();
+ if (fStack.isEmpty()) {
+ fResult.append(VARIABLE_START);
+ fResult.append(tos.getText());
+ } else {
+ VariableReference var = (VariableReference)fStack.peek();
+ var.append(VARIABLE_START);
+ var.append(tos.getText());
+ }
+ }
+
+
+ return resolvedVariables;
+ }
+
+ /**
+ * Resolve and return the value of the given variable reference,
+ * possibly <code>null</code>.
+ *
+ * @param var
+ * @param reportUndefinedVariables whether to report undefined variables as
+ * an error
+ * @return variable value, possibly <code>null</code>
+ * @exception CoreException if unable to resolve a value
+ */
+ private String resolve(VariableReference var, boolean reportUndefinedVariables) throws CoreException {
+// String text = var.getText();
+// int pos = text.indexOf(VARIABLE_ARG);
+// String name = null;
+// String arg = null;
+// if (pos > 0) {
+// name = text.substring(0, pos);
+// pos++;
+// if (pos < text.length()) {
+// arg = text.substring(pos);
+// }
+// } else {
+// name = text;
+// }
+// IValueVariable valueVariable = manager.getValueVariable(name);
+// if (valueVariable == null) {
+// IDynamicVariable dynamicVariable = manager.getDynamicVariable(name);
+// if (dynamicVariable == null) {
+// // no variables with the given name
+// if (reportUndefinedVariables) {
+// throw new CoreException(new Status(IStatus.ERROR, VariablesPlugin.getUniqueIdentifier(), VariablesPlugin.INTERNAL_ERROR, NLS.bind(VariablesMessages.StringSubstitutionEngine_3, new String[]{name}), null));
+// }
+// // leave as is
+// return getOriginalVarText(var);
+// }
+//
+// if (resolveVariables) {
+// fSubs = true;
+// return dynamicVariable.getValue(arg);
+// }
+// //leave as is
+// return getOriginalVarText(var);
+// }
+//
+// if (arg == null) {
+// if (resolveVariables) {
+// fSubs = true;
+// return valueVariable.getValue();
+// }
+// //leave as is
+// return getOriginalVarText(var);
+// }
+// // error - an argument specified for a value variable
+// throw new CoreException(new Status(IStatus.ERROR, VariablesPlugin.getUniqueIdentifier(), VariablesPlugin.INTERNAL_ERROR, NLS.bind(VariablesMessages.StringSubstitutionEngine_4, new String[]{valueVariable.getName()}), null));
+ return "";
+ }
+
+ private String getOriginalVarText(VariableReference var) {
+ StringBuffer res = new StringBuffer(var.getText());
+ res.insert(0, VARIABLE_START);
+ res.append(VARIABLE_END);
+ return res.toString();
+ }
+}
_______________________________________________
jbosstools-commits mailing list
jbosstools-commits(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/jbosstools-commits
--
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
16 years, 11 months
Fwd: [jbosstools-commits] JBoss Tools SVN: r5362 - trunk/seam/plugins/org.jboss.tools.seam.core/src/org/jboss/tools/seam/internal/core/validation.
by Max Rydahl Andersen
Parsing every file when there is an error !? I find that hard to belive
being a proper solution to this.
It will definitly not scale - you can have hundreds even thousands of
warnings and each of them parsing that file yet another time is not a good
way.
Is that how all other validators does it ?
-max
------- Forwarded message -------
From: jbosstools-commits(a)lists.jboss.org
To: jbosstools-commits(a)lists.jboss.org
Cc:
Subject: [jbosstools-commits] JBoss Tools SVN: r5362 -
trunk/seam/plugins/org.jboss.tools.seam.core/src/org/jboss/tools/seam/internal/core/validation.
Date: Tue, 18 Dec 2007 18:25:47 +0100
Author: dazarov
Date: 2007-12-18 12:25:47 -0500 (Tue, 18 Dec 2007)
New Revision: 5362
Modified:
trunk/seam/plugins/org.jboss.tools.seam.core/src/org/jboss/tools/seam/internal/core/validation/SeamValidator.java
Log:
http://jira.jboss.com/jira/browse/JBIDE-1489
Modified:
trunk/seam/plugins/org.jboss.tools.seam.core/src/org/jboss/tools/seam/internal/core/validation/SeamValidator.java
===================================================================
---
trunk/seam/plugins/org.jboss.tools.seam.core/src/org/jboss/tools/seam/internal/core/validation/SeamValidator.java 2007-12-18
16:41:55 UTC (rev 5361)
+++
trunk/seam/plugins/org.jboss.tools.seam.core/src/org/jboss/tools/seam/internal/core/validation/SeamValidator.java 2007-12-18
17:25:47 UTC (rev 5362)
@@ -12,15 +12,21 @@
import java.util.Set;
+
+import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.wst.validation.internal.core.Message;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
+import org.jboss.tools.common.util.FileUtil;
import org.jboss.tools.seam.core.ISeamProject;
import org.jboss.tools.seam.core.ISeamTextSourceReference;
+import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.core.SeamPreferences;
+import org.eclipse.jface.text.Document;
/**
* Abstract implementation of ISeamvalidator
@@ -71,10 +77,40 @@
IMessage message = new Message(getBaseName(), messageSeverity,
messageId, messageArguments, target,
ISeamValidator.MARKED_SEAM_RESOURCE_MESSAGE_GROUP);
message.setLength(length);
message.setOffset(offset);
+ int lineNumber = getLineNumber(target, offset);
+ if(lineNumber != 0)message.setLineNo(lineNumber);
if(!ignore) {
reporter.addMessage(validationManager, message);
}
}
+
+ private int getLineNumber(IResource resource, int offset){
+ if(resource.getType() == IResource.FILE){
+ IFile file = (IFile)resource;
+ String content;
+ try {
+ if(!file.isSynchronized(IResource.DEPTH_ZERO)) {
+ // The resource is out of sync with the file system
+ // Just ignore this resource.
+ return 0;
+ }
+ content = FileUtil.readStream(file.getContents());
+ } catch (CoreException e) {
+ SeamCorePlugin.getPluginLog().logError(e);
+ return 0;
+ }
+ Document document = new Document(content);
+ int lineNumber=0;
+ try{
+ lineNumber = document.getLineOfOffset(offset);
+ return lineNumber+1;
+ }catch(Exception e){
+ SeamCorePlugin.getPluginLog().logError(e);
+ return 0;
+ }
+ }
+ return 0;
+ }
protected void displaySubtask(String messageId) {
displaySubtask(messageId, null);
_______________________________________________
jbosstools-commits mailing list
jbosstools-commits(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/jbosstools-commits
--
--
Max Rydahl Andersen
callto://max.rydahl.andersen
Hibernate
max(a)hibernate.org
http://hibernate.org
JBoss a division of Red Hat
max.andersen(a)jboss.com
17 years