All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jdo.api.persistence.model.util.ModelValidator Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portion Copyright [2018-2019] Payara Foundation and/or affiliates

/*
 * ModelValidator.java
 *
 * Created on September 22, 2000, 12:49 PM
 */

package com.sun.jdo.api.persistence.model.util;

import com.sun.jdo.api.persistence.model.Model;
import com.sun.jdo.api.persistence.model.jdo.PersistenceClassElement;
import com.sun.jdo.api.persistence.model.jdo.PersistenceFieldElement;
import com.sun.jdo.api.persistence.model.jdo.RelationshipElement;
import com.sun.jdo.api.persistence.model.mapping.*;
import com.sun.jdo.spi.persistence.utility.JavaTypeHelper;
import com.sun.jdo.spi.persistence.utility.logging.Logger;
import org.glassfish.common.util.StringHelper;
import org.glassfish.persistence.common.I18NHelper;
import org.netbeans.modules.dbschema.*;
import org.netbeans.modules.dbschema.util.NameUtil;
import org.netbeans.modules.dbschema.util.SQLTypeUtil;

import java.lang.reflect.Modifier;
import java.util.*;

/**
 *
 * @author Rochelle Raccah
 * @version %I%
 */
public class ModelValidator
{
	/** This field holds the model object used for validation */
	private Model _model;

	/** This field holds the name of the class being validated */
	private String _className;

	/** This field holds the class loader used to load class
	 * being validated (if available).
	 */
	private ClassLoader _classLoader;

	/** I18N message handler */
	private ResourceBundle _messages;

	public ModelValidator (Model model, String className, ResourceBundle bundle)
	{
		this(model, className, null, bundle);
	}

 	/** Create a new model validator object.
	 * @param model model object used for validation
	 * @param className the name of the class being validated
	 */
	public ModelValidator (Model model, String className,
		ClassLoader classLoader, ResourceBundle bundle)
	{
		_model = model;
		_className = className;
		_classLoader = classLoader;
		_messages = bundle;
	}

	/**
	 * Get the model object used for validation.
	 * @return the model object used for validation
	 */
	public Model getModel () { return _model; }

	/**
	 * Get the name of the class being validated.
	 * @return the name of the class being validated
	 */
	public String getClassName () { return _className; }

	/**
	 * Get the class loader used to load the class being validated.
	 * @return the class loader of the class being validated
	 */
	public ClassLoader getClassLoader () { return _classLoader; }

	/** @return I18N message handler for this element
	 */
	protected ResourceBundle getMessages () { return _messages; }

	/** Main method used for parsing the combination of java (or class)
	 * information and mapping/jdo information by running through a subset
	 * of the full validation check and aborting (and returning
	 * false at the first error or warning.
	 * @return true if no errors or warnings occur,
	 * false otherwise.
	 * @see #getBasicValidationList
	 */
	public boolean parseCheck ()
	{
		Iterator iterator = getBasicValidationList().iterator();

		try
		{
			while (iterator.hasNext())
				((ValidationComponent)iterator.next()).validate();
		}
		catch (ModelValidationException e)
		{
			LogHelperModel.getLogger().log(Logger.FINER,
				"model.parse_error", e);	// NOI18N

			return false;
		}

		return true;
	}

	/** Main method used for validating the combination of java (or class)
	 * information and mapping/jdo information by running through the full
	 * validation check and returning a collection of
	 * ModelValidationExceptions containing any errors or warnings encountered.
	 * @return a collection of ModelValidationExceptions containing any
	 * errors or warnings encountered.  If no errors or warnings were
	 * encountered, the collection will be empty, not null.
	 * @see #getFullValidationList
	 */
	public Collection fullValidationCheck ()
	{
		ArrayList list = new ArrayList();
		Iterator iterator = getFullValidationList().iterator();

		while (iterator.hasNext())
		{
			try
			{
				((ValidationComponent)iterator.next()).validate();
			}
			catch (ModelValidationException e)
			{
				list.add(e);
			}
		}

		return Collections.unmodifiableCollection(list);
	}

	// ================ Validation list construction methods ===============

	/** Computes and returns a collection of ValidationComponents
	 * representing the tests to be performed during parse.
	 * @return a collection of ValidationComponents representing the
	 * tests to be performed during parse.
	 * @see #getDatabaseValidationList
	 * @see #getFieldsValidationList
	 * @see #getFullValidationList
	 */
	public Collection getBasicValidationList ()
	{
		ArrayList list = new ArrayList();
		String className = getClassName();

		list.add(createClassExistenceComponent(className));
		list.add(createClassPersistenceComponent(className));

		list.addAll(getDatabaseValidationList());
		list.addAll(getFieldsValidationList());

		return Collections.unmodifiableCollection(list);
	}

	/** Computes and returns a collection of ValidationComponents
	 * representing the tests to be performed during validation.  These
	 * include all those in the basic list plus those which check
	 * cardinality and the related classes in more detail.
	 * @return a collection of ValidationComponents representing the
	 * tests to be performed during validation.
	 * @see #getRelatedClassValidationList
	 * @see #getBasicValidationList
	 */
	public Collection getFullValidationList ()
	{
		ArrayList list = new ArrayList(getBasicValidationList());
		String className = getClassName();
		PersistenceClassElement persistenceClass =
			getPersistenceClass(className);

		if (persistenceClass != null)
		{
			PersistenceFieldElement[] fields = persistenceClass.getFields();
			int i, count = ((fields != null) ? fields.length : 0);

			list.add(createSerializableClassComponent(className));
			list.add(createKeyClassComponent(persistenceClass.getKeyClass()));
			list.add(createClassMappingComponent(persistenceClass));
			list.add(createKeyColumnMappingComponent(persistenceClass));

			for (i = 0; i < count; i++)
			{
				PersistenceFieldElement field = fields[i];

				list.add(createFieldCardinalityComponent(field));
				list.add(createFieldMappingComponent(field));
				list.add(createFieldBlobMappingComponent(field));
				list.addAll(getRelatedClassValidationList(field));
			}
		}

		return Collections.unmodifiableCollection(list);
	}

	// ============= Validation list construction suppport methods ============

	/** Computes and returns a collection of ValidationComponents
	 * representing the database tests to be performed.
	 * @return a collection of ValidationComponents representing the
	 * database tests to be performed.
	 */
	private Collection getDatabaseValidationList ()
	{
		ArrayList list = new ArrayList();
		String className = getClassName();
		MappingClassElement mappingClass = getMappingClass(className);

		if (mappingClass != null)
		{
			ArrayList tables = mappingClass.getTables();
			int i, count = ((tables != null) ? tables.size() : 0);
			MappingTableElement primaryTable = null;
			Iterator iterator = null;

			list.add(createSchemaExistenceComponent(className));

			for (i = 0; i < count; i++)
			{
				MappingTableElement nextTable =
					(MappingTableElement)tables.get(i);

				list.add(createTableExistenceComponent(nextTable.getTable()));

				if (i == 0)
				{
					primaryTable = nextTable;
					list.add(createPrimaryTableComponent(primaryTable));
				}
				else
				{
					MappingReferenceKeyElement referenceKey =
						findReferenceKey(primaryTable, nextTable);

					if (referenceKey != null)
					{
						iterator = referenceKey.getColumnPairNames().iterator();
						while (iterator.hasNext())
						{
							list.add(createColumnExistenceComponent(
								(String)iterator.next()));
						}
					}
				}
			}

			list.add(createVersionConsistencyComponent(mappingClass));

			iterator = mappingClass.getFields().iterator();
			while (iterator.hasNext())
			{
				MappingFieldElement nextField =
					(MappingFieldElement)iterator.next();
				ArrayList allColumns = new ArrayList();
				Iterator columnIterator = null;

				if (isRelationship(nextField))
				{
					allColumns.addAll(((MappingRelationshipElement)nextField).
						getAssociatedColumns());
				}

				allColumns.addAll(nextField.getColumns());

				columnIterator = allColumns.iterator();
				while (columnIterator.hasNext())
				{
					list.add(createColumnExistenceComponent(
						(String)columnIterator.next(), nextField));
				}
			}
		}

		return list;
	}

	/** Computes and returns a collection of ValidationComponents
	 * representing the field and relationship tests to be performed.
	 * @return a collection of ValidationComponents representing the
	 * field and relationship tests to be performed.
	 */
	private Collection getFieldsValidationList ()
	{
		ArrayList list = new ArrayList();
		Model model = getModel();
		String className = getClassName();
		PersistenceClassElement persistenceClass =
			getPersistenceClass(className);

		if (persistenceClass != null)
		{
			PersistenceFieldElement[] fields = persistenceClass.getFields();
			int i, count = ((fields != null) ? fields.length : 0);
			Iterator iterator =
				getMappingClass(className).getFields().iterator();

			for (i = 0; i < count; i++)
			{
				PersistenceFieldElement field = fields[i];

				list.add(createFieldExistenceComponent(field));

				// even though this is really the validation step, we
				// only want to add the others if the field exists
				if (model.hasField(className, field.getName()))
				{
					list.add(createFieldPersistenceComponent(field));
					list.add(createFieldPersistenceTypeComponent(field));
					list.add(createFieldConsistencyComponent(field));

					if (isLegalRelationship(field))
					{
						RelationshipElement rel = (RelationshipElement)field;

						/* user modifiable collection class not yet supported
						list.add(createCollectionClassComponent(rel));*/
						list.add(createElementClassComponent(rel));
						list.add(createRelatedClassMatchesComponent(rel));
					}
				}
			}

			while (iterator.hasNext())
			{
				MappingFieldElement field =
					(MappingFieldElement)iterator.next();
				String fieldName = field.getName();

				// only check this if it is not in the jdo model
				if (persistenceClass.getField(fieldName) == null)
				{
					list.add(createFieldExistenceComponent(field));

					// even though this is really the validation step, we
					// only want to add the others if the field exists
					if (model.hasField(className, fieldName))
						list.add(createFieldConsistencyComponent(field));
				}

				if (!isRelationship(field))
					list.add(createColumnOverlapComponent(field));

				// preliminary fix for CR6239630
				if (Boolean.getBoolean("AllowManagedFieldsInDefaultFetchGroup")) // NOI18N
		 		{
                                    // Do nothing - AllowManagedFieldsInDefaultFetchGroup:
                                    // disabled single model validation test;
                                    // may use checked read/write access to managed fields
				}
				else
				{
					list.add(createFieldDefaultFetchGroupComponent(field));
				}
			}
		}

		return list;
	}

	/** Computes and returns a collection of ValidationComponents
	 * representing the related class tests to be performed.  Right now,
	 * these are only included as part of full validation, as they may
	 * be somewhat time intensive since they compute information about
	 * other classes as well as this class.
	 * @return a collection of ValidationComponents representing the
	 * related class tests to be performed.
	 */
	private Collection getRelatedClassValidationList (
		PersistenceFieldElement field)
	{
		String relatedClass = getRelatedClass(field);
		ArrayList list = new ArrayList();

		// even though this is really already included in the validation
		// step, we only want to add the extra steps if the field exists
		if ((relatedClass != null) &&
			getModel().hasField(getClassName(), field.getName()))
		{
			MappingClassElement relatedClassElement =
				getMappingClass(relatedClass);

			list.add(createClassExistenceComponent(relatedClass, field));
			list.add(createClassPersistenceComponent(relatedClass, field));
			list.add(createSchemaExistenceComponent(relatedClass, field));
			list.add(createRelatedSchemaMatchesComponent(relatedClass, field));

			if (relatedClassElement != null)
			{
				ArrayList tables = relatedClassElement.getTables();
				MappingTableElement primaryTable = null;
				boolean hasTables = ((tables != null) && (tables.size() > 0));

				if (hasTables)
				{
					primaryTable = (MappingTableElement)tables.get(0);
					list.add(createTableExistenceComponent(
						primaryTable.getTable(), field));
				}

				if (isRelationship(field))
				{
					RelationshipElement relElement = (RelationshipElement)field;
					Object rel = getMappingClass(getClassName()).
						getField(field.getName());

					list.add(createInverseFieldComponent(relElement));
					list.add(createInverseMappingComponent(relElement));

					// verify that the columns from the primary table
					// of the related class are actually from that table
					// since it could have been changed
					if ((rel != null) && isRelationship(rel))
					{
						MappingRelationshipElement relationship =
							(MappingRelationshipElement)rel;
						ArrayList columns =
							relationship.getAssociatedColumns();
						Iterator iterator = null;

						if ((columns == null) || (columns.size() == 0))
							columns = relationship.getColumns();

						if (columns != null)
						{
							List tableNames = new ArrayList();

							if (hasTables)
							{
								Iterator tableIterator = tables.iterator();

								while (tableIterator.hasNext())
								{
									tableNames.add(((MappingTableElement)
										tableIterator.next()).getName());
								}
							}

							iterator = columns.iterator();

							while (iterator.hasNext())
							{
								list.add(createRelatedTableMatchesComponent(
									relatedClass, field, tableNames,
									(String)iterator.next()));
							}
						}
					}
				}
			}
		}

		return list;
	}

	// ================ Validation Component inner classes ===============

	/** Create a validation component which can check whether the class exists.
	 * @param className the class whose existence is being checked
	 * @param relatedField the relationship field whose class is being checked,
	 * may be null in which case we are probably checking the
	 * same class as the validator is checking overall
	 * @return the validation component
	 */
	protected ValidationComponent createClassExistenceComponent (
		final String className, final PersistenceFieldElement relatedField)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				if ((className == null) ||
					!getModel().hasClass(className, getClassLoader()))
				{
					throw constructClassException(className, relatedField,
						"util.validation.class_not_found");		//NOI18N
				}
			}
		};
	}

	/** Create a validation component which can check whether the class exists.
	 * @param className the class whose existence is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createClassExistenceComponent (
		final String className)
	{
		return createClassExistenceComponent(className, null);
	}

	/** Create a validation component which can check the class persistence.
	 * @param className the class whose persistence is being checked
	 * @param relatedField the relationship field whose class is being checked,
	 * may be null in which case we are probably checking the
	 * same class as the validator is checking overall
	 * @return the validation component
	 */
	protected ValidationComponent createClassPersistenceComponent (
		final String className, final PersistenceFieldElement relatedField)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				Model model = getModel();

				if ((className != null) &&
					 model.hasClass(className, getClassLoader()))
				{
					String key = null;

					if (!isPersistent(className))
						key = "util.validation.class_not_persistence_capable";//NOI18N
					else if (!model.isPersistenceCapableAllowed(className))
						key = "util.validation.class_not_allowed";//NOI18N

					if (key != null)
					{
						throw constructClassException(
							className, relatedField, key);
					}
				}
			}
		};
	}

	/** Create a validation component which can check the class persistence.
	 * @param className the class whose persistence is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createClassPersistenceComponent (
		final String className)
	{
		return createClassPersistenceComponent(className, null);
	}

	/** Create a validation component which can check whether the field exists.
	 * @param fieldName the field whose existence is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createFieldExistenceComponent (
		final String fieldName)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				if (!getModel().hasField(getClassName(), fieldName))
				{
					throw constructFieldException(fieldName,
						"util.validation.field_not_found");			//NOI18N
				}
			}
		};
	}

	/** Create a validation component which can check whether the field exists.
	 * @param field the field whose existence is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createFieldExistenceComponent (Object field)
	{
		return createFieldExistenceComponent(field.toString());
	}

	/** Create a validation component which can check whether the field is
	 * persistent.
	 * @param field the field whose persistence is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createFieldPersistenceComponent (
		final PersistenceFieldElement field)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				boolean isPersistent = (PersistenceFieldElement.PERSISTENT ==
					field.getPersistenceType());
				String fieldName = field.getName();

				if (isPersistent &&
					!isPersistentAllowed(getClassName(), fieldName))
				{
					throw constructFieldException(fieldName,
						"util.validation.field_persistent_not_allowed");//NOI18N
				}
			}
		};
	}

	/** Create a validation component which can check whether the field is
	 * consistent (field in both models or relationship in both).
	 * @param field the field whose consistency is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createFieldConsistencyComponent (
		final PersistenceFieldElement field)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				String fieldName = field.getName();
				String className = getClassName();
				boolean isLegallyPersistent =
					isPersistentAllowed(className, fieldName);

				if (isLegallyPersistent)
				{
					MappingClassElement mappingClass =
						getMappingClass(className);
					MappingFieldElement mappingElement =
						((mappingClass != null) ?
						mappingClass.getField(fieldName) : null);

					if (mappingElement != null)
					{
						boolean jdoIsRelationship = isLegalRelationship(field);

						if (jdoIsRelationship != isRelationship(mappingElement))
						{
							throw constructFieldException(fieldName,
								"util.validation.field_type_inconsistent");	//NOI18N
						}
					}
				}
			}
		};
	}

	/** Create a validation component which can check whether the field is
	 * consistent (if in mapping model but not jdo, it is a problem).
	 * @param field the field whose consistency is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createFieldConsistencyComponent (
		final MappingFieldElement field)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				if (field != null)
				{
					String fieldName = field.getName();
					PersistenceClassElement persistenceClass =
						getPersistenceClass(getClassName());
					PersistenceFieldElement persistenceElement =
						((persistenceClass != null) ?
						persistenceClass.getField(fieldName) : null);

					if (persistenceElement == null)
					{
						throw constructFieldException(fieldName,
							"util.validation.field_model_inconsistent");//NOI18N
					}
				}
			}
		};
	}

	/** Create a validation component which can check the persistence type
	 * of the field (whether it is a relationship or not).
	 * @param field the field whose persistence type is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createFieldPersistenceTypeComponent (
		final PersistenceFieldElement field)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				String fieldName = field.getName();
				String className = getClassName();
				boolean isLegallyPersistent =
					isPersistentAllowed(className, fieldName);

				if (isLegallyPersistent)
				{
					boolean isRelationship = isRelationship(field);
					boolean mustBeRelationship = shouldBeRelationship(field);

					if (isRelationship && !mustBeRelationship)
					{
						throw constructFieldException(fieldName,
							"util.validation.field_relationship_not_allowed");//NOI18N
					}
					else if (!isRelationship && mustBeRelationship)
					{
						throw constructFieldException(fieldName,
							"util.validation.field_type_not_allowed");	//NOI18N
					}
				}
			}
		};
	}

	/** Create a validation component which can check whether the cardinality
	 * bounds are semantically valid given the relationship field type.
	 * @param field the relationship whose cardinality bounds are being checked
	 * @return the validation component
	 */
	protected ValidationComponent createFieldCardinalityComponent (
		final PersistenceFieldElement field)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				if (isLegalRelationship(field))
				{
					RelationshipElement relationship =
						(RelationshipElement)field;
					String fieldName = field.getName();
					boolean nonCollectionRelationship =
						!isCollection(getClassName(), fieldName);
					int upperBound = (nonCollectionRelationship ?
						1 : relationship.getUpperBound());
					int lowerBound = relationship.getLowerBound();
					MappingRelationshipElement mapping = null;

					if ((lowerBound < 0) || (upperBound <= 0) ||
						(lowerBound > upperBound))
					{
						throw constructFieldException(fieldName,
							"util.validation.cardinality_invalid");	//NOI18N
					}

					// now check specific lower bound requirements imposed
					// by the mapping
					mapping = getMappingRelationship(relationship);
					if (nonCollectionRelationship && (lowerBound != 1) &&
						(mapping != null) && !isJoin(mapping))
					{
						// If the non-collection relationship field is exactly
						// mapped to a FK, we need to check the nullability
						// of the columns. If there are any non-nullable
						// columns the lower bound must be 1.
						ForeignKeyElement fk = getMatchingFK(mapping);

						if ((fk != null) && hasNonNullableColumn(fk))
						{
							throw constructFieldException(fieldName,
								"util.validation.lower_bound_invalid"); //NOI18N
						}
					}
				}
			}

			/** Returns true if the specified FK has at least
			 * one non-nullable column. Please note that the caller is
			 * responsible for passing a non-null fk argument.
			 */
			private boolean hasNonNullableColumn (ForeignKeyElement fk)
			{
				ColumnElement[] localColumns = fk.getLocalColumns();
				int count = ((localColumns != null) ? localColumns.length : 0);

				for (int i = 0; i < count; i++)
				{
					if (!localColumns[i].isNullable())
						return true;
				}

				return false;
			}

			/** Checks whether the specified relationship is exactly mapped
			 * to a FK. If yes, the method returns the matching FK. If not,
			 * it returns null. Please note that the caller is
			 * responsible for passing a non-null mapping argument.
			 */
 			private ForeignKeyElement getMatchingFK (
				MappingRelationshipElement mapping)
			{
				MappingClassElement mappingClass = mapping.
					getDeclaringClass();
				String databaseRoot = getSchemaForClass(getClassName());
				List pairNames = mapping.getColumns();
				List tables = mappingClass.getTables();

				if (tables != null)
				{
					for (Iterator i = tables.iterator(); i.hasNext();)
					{
						String tableName = ((MappingTableElement)i.next()).
							getName();
						TableElement table = getTable(tableName, databaseRoot);
						ForeignKeyElement fk = getMatchingFK(pairNames, table);

						if (fk != null)
							return fk;
					}
				}

				return null;
			}

			/** Checks whether the specified TableElement has a FK that
			 * exactly matches the list of column pair names.
			 * @return the matching FK if it exactly matches the list
			 * of column pairs; null otherwise.
			 */
			private ForeignKeyElement getMatchingFK (List pairNames,
				TableElement table)
			{
				ForeignKeyElement[] foreignKeys = (table != null) ?
					table.getForeignKeys() : null;
				int count = ((foreignKeys != null) ? foreignKeys.length : 0);

				for (int i = 0; i < count; i++)
				{
					if (matchesFK(pairNames, foreignKeys[i]))
						return foreignKeys[i];
				}

				return null;
			}

			/** Returns true if the specified list of column
			 * pair names matches exactly the specified FK.
			 */
			private boolean matchesFK (List pairNames,
				ForeignKeyElement foreignKey)
			{
				ColumnPairElement[] fkPairs = foreignKey.getColumnPairs();
				int fkCount = ((fkPairs != null) ? fkPairs.length : 0);
				int count = ((pairNames != null) ? pairNames.size() : 0);

				// First check whether the list of fk column pairs has the
				// same size than the specified list of columns.
				if (fkCount == count)
				{
					// Now check whether each fk column is included in the
					// specified list of columns.
					for (int i = 0; i < fkCount; i++)
					{
						String fkPairName = NameUtil.getRelativeMemberName(
							fkPairs[i].getName().getFullName());

						if (!pairNames.contains(fkPairName))
							return false;
					}

					return true;
				}

				return false;
			}
		};
	}

	/** Create a validation component which can check whether the field is
	 * unmapped.
	 * @param field the field whose mapping is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createFieldMappingComponent (
		final PersistenceFieldElement field)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				String fieldName = field.getName();
				MappingClassElement mappingClass =
					getMappingClass(getClassName());

				if ((mappingClass != null) &&
					(mappingClass.getTables().size() > 0))
				{
					MappingFieldElement mappingField =
						mappingClass.getField(fieldName);

					if ((mappingField == null) ||
						(mappingField.getColumns().size() == 0))
					{
						throw constructFieldException(
							ModelValidationException.WARNING, fieldName,
							"util.validation.field_not_mapped");	//NOI18N
					}
				}
			}
		};
	}

	/** Create a validation component which can check whether the field is
	 * mapped to a blob type and if so, whether it is a key field or belongs
	 * to the default fetch group.  Note that it's somewhat important to check
	 * for the key field first because if a field is key, its fetch group
	 * value in the model is ignored.
	 * @param field the field whose mapping/key field/fetch group consistency
	 * is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createFieldBlobMappingComponent (
		final PersistenceFieldElement field)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				String className = getClassName();
				String fieldName = field.getName();
				MappingClassElement mappingClass = getMappingClass(className);
				MappingFieldElement mappingField = ((mappingClass != null) ?
					mappingClass.getField(fieldName) : null);

				if (mappingField != null)
				{
					boolean isKey = field.isKey();

 					if (isKey || (MappingFieldElement.GROUP_DEFAULT ==
						mappingField.getFetchGroup()))
					{
						if (isMappedToBlob(mappingField,
							getSchemaForClass(className)))
						{
							throw constructFieldException(fieldName, (isKey ?
								"util.validation.field_key_field_not_allowed" : //NOI18N
								"util.validation.field_fetch_group_not_allowed")); // NOI18N
						}
					}
				}
			}
			private boolean isMappedToBlob (MappingFieldElement mappingField,
				String schema)
			{
				if (mappingField instanceof MappingRelationshipElement)
				{
					return isMappedToBlob(
						(MappingRelationshipElement)mappingField, schema);
				}
				else
				{
					Iterator iterator = mappingField.getColumns().iterator();

					while (iterator.hasNext())
					{
						String absoluteName = NameUtil.getAbsoluteMemberName(
							schema, (String)iterator.next());
						TableElement table = TableElement.forName(
							NameUtil.getTableName(absoluteName));
						ColumnElement columnElement = ((table != null) ?
							(ColumnElement)table.getMember(
							DBIdentifier.create(absoluteName)) : null);

						if (isMappedToBlob(columnElement))
							return true;
					}
				}

				return false;
			}
			private boolean isMappedToBlob (MappingRelationshipElement rel,
				String schema)
			{
				Iterator iterator = rel.getColumns().iterator();

				while (iterator.hasNext())
				{
					ColumnPairElement pair =
						getPair((String)iterator.next(), schema);

					if (isMappedToBlob(pair))
						return true;
				}

				// now check join columns
				iterator = rel.getAssociatedColumns().iterator();
				while (iterator.hasNext())
				{
					ColumnPairElement pair =
						getPair((String)iterator.next(), schema);

					if (isMappedToBlob(pair))
						return true;
				}

				return false;
			}
			private boolean isMappedToBlob (ColumnPairElement pair)
			{
				return ((pair == null) ? false :
					isMappedToBlob(pair.getLocalColumn()) &&
					isMappedToBlob(pair.getReferencedColumn()));
			}
			private boolean isMappedToBlob (ColumnElement column)
			{
				return ((column != null) &&
					SQLTypeUtil.isBlob(column.getType()));
			}
		};
	}

	/** Create a validation component which can check whether the collection
	 * class is valid given the relationship field type.
	 * @param field the relationship whose collection class is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createCollectionClassComponent (
		final RelationshipElement field)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				String className = getClassName();
				String fieldName = field.getName();

				if (isCollection(className, fieldName))
				{
					Model model = getModel();
					String collectionClass = field.getCollectionClass();
					String fieldType = model.getFieldType(className, fieldName);
					boolean missingCollectionClass =
						StringHelper.isEmpty(collectionClass);

					if (!missingCollectionClass &&
						!model.getSupportedCollectionClasses(fieldType).
						contains(collectionClass))
					{
						throw constructFieldException(fieldName,
							"util.validation.collection_class_invalid");//NOI18N
					}
				}
			}
		};
	}

	/** Create a validation component which can check whether the
	 * relationship is mapped to columns even though the element class is null.
	 * @param field the relationship whose element class is being checked
	 * @return the validation component
	 */
	protected ValidationComponent createElementClassComponent (
		final RelationshipElement field)
	{
		return new ValidationComponent ()
		{
			public void validate () throws ModelValidationException
			{
				String className = getClassName();
				String fieldName = field.getName();

				if (isCollection(className, fieldName))
				{
					String elementClass = field.getElementClass();

					if (StringHelper.isEmpty(elementClass))
					{
						MappingClassElement mappingClass =
							getMappingClass(className);
						MappingFieldElement mappingElement =
							((mappingClass != null) ?
							mappingClass.getField(fieldName) : null);

						if ((mappingElement != null) &&
							(mappingElement.getColumns().size() > 0))
						{
							throw constructFieldException(fieldName,
								"util.validation.element_class_not_found");//NOI18N
						}
					}
				}
			}
		};
	}

	/** Create a validation component which checks whether the rules for
	 * version consistency are followed.  This includes:
	 * 
    *
  • There must be exactly one version field defined. *
  • The version field must not be a relationship. *
  • The version field must not be a key field. *
  • The version field must be of java type (primitive) long. *
  • The version field must be in the default fetch group. *
  • The version field must be mapped to exactly 1 column from the * primary table. *
  • The column to which the version field is mapped must be of a * numeric type and non-nullable. *
  • The column to which the version field is mapped must not be a PK or * FK column. *
* @param mappingClass the mapping class element whose consistency is being * checked * @return the validation component */ protected ValidationComponent createVersionConsistencyComponent ( final MappingClassElement mappingClass) { return new ValidationComponent () { public void validate () throws ModelValidationException { // only bother to check for classes with version consistency if (MappingClassElement.VERSION_CONSISTENCY == mappingClass.getConsistencyLevel()) { MappingFieldElement versionField = validateVersionFieldExistence(); String className = mappingClass.getName(); String fieldName = versionField.getName(); String columnName = null; ColumnElement column = null; if (versionField instanceof MappingRelationshipElement) { throw constructFieldException(fieldName, "util.validation.version_field_relationship_not_allowed");//NOI18N } else if (MappingFieldElement.GROUP_DEFAULT != versionField.getFetchGroup()) // must be in DFG { throw constructFieldException(fieldName, "util.validation.version_field_fetch_group_invalid");//NOI18N } validatePersistenceFieldAttributes(className, fieldName); columnName = validateVersionFieldMapping(versionField); column = validateTableMatch(className, fieldName, columnName); validateColumnAttributes(className, fieldName, column); } } /** Helper method validating the existence of the exactly one * version field. */ private MappingFieldElement validateVersionFieldExistence () throws ModelValidationException { List versionFields = mappingClass.getVersionFields(); // must have exactly 1 version field (for this release) if (versionFields.size() != 1) { throw constructClassException(mappingClass.getName(), null, "util.validation.version_field_cardinality"); //NOI18N } return (MappingFieldElement)versionFields.get(0); } /** Helper method validating the attributes of the field in the * jdo model which corresponds to the version field. */ private void validatePersistenceFieldAttributes (String className, String fieldName) throws ModelValidationException { Class fieldType = JavaTypeHelper.getPrimitiveClass( getModel().getFieldType(className, fieldName)); String keyName = null; // must not be a key field if (getPersistenceClass(className).getField(fieldName).isKey()) keyName = "util.validation.version_field_key_field_not_allowed";//NOI18N else if (Long.TYPE != fieldType) // must be type long keyName = "util.validation.version_field_type_not_allowed";//NOI18N if (keyName != null) throw constructFieldException(fieldName, keyName); } /** Helper method validating the column name of the * version field mapping. */ private String validateVersionFieldMapping ( MappingFieldElement versionField) throws ModelValidationException { List columns = versionField.getColumns(); // must be mapped to exactly 1 column (for this release) if (columns.size() != 1) { throw constructFieldException(versionField.getName(), "util.validation.version_field_not_mapped"); //NOI18N } return (String)columns.get(0); } /** Helper method validating the column mapping of the version * field is from the primary table. */ private ColumnElement validateTableMatch (String className, String fieldName, String columnName) throws ModelValidationException { String schema = getSchemaForClass(className); String absoluteName = NameUtil.getAbsoluteMemberName(schema, columnName); TableElement table = TableElement.forName(NameUtil.getTableName(absoluteName)); String primaryName = ((MappingTableElement)mappingClass. getTables().get(0)).getName(); TableElement pTable = getTable(primaryName, schema); // column must be from the PT if (table != pTable) { throw new ModelValidationException( getModel().getField(className, fieldName), I18NHelper.getMessage(getMessages(), "util.validation.version_field_table_mismatch", //NOI18N new Object[]{columnName, fieldName, className})); } return ((table != null) ? (ColumnElement)table.getMember( DBIdentifier.create(absoluteName)) : null); } /** Helper method validating the attributes of the column of the * version field mapping. */ private void validateColumnAttributes (String className, String fieldName, ColumnElement column) throws ModelValidationException { String keyName = null; // column must be numeric type and non-nullable if (column.isNullable() || !column.isNumericType()) keyName = "util.validation.version_field_column_type_invalid"; // NOI18N else // column must be non-PK and non-FK column { TableElement table = column.getDeclaringTable(); UniqueKeyElement[] uks = table.getUniqueKeys(); ForeignKeyElement[] fks = table.getForeignKeys(); int i, count = ((uks != null) ? uks.length : 0); for (i = 0; i < count; i++) { UniqueKeyElement uk = uks[i]; if (uk.isPrimaryKey() && Arrays.asList( uk.getColumns()).contains(column)) { keyName = "util.validation.version_field_column_pk_invalid"; // NOI18N break; } } count = ((fks != null) ? fks.length : 0); for (i = 0; i < count; i++) { ForeignKeyElement fk = fks[i]; if (Arrays.asList(fk.getLocalColumns()). contains(column)) { keyName = "util.validation.version_field_column_fk_invalid"; // NOI18N break; } } } if (keyName != null) { throw new ModelValidationException( getModel().getField(className, fieldName), I18NHelper.getMessage(getMessages(), keyName, new Object[]{column.getName(), fieldName, className})); } } }; } /** Create a validation component which can check whether the inverse of * the inverse of the relationship is the relationship itself. * @param field the relationship whose inverse relationship is being checked * @return the validation component */ protected ValidationComponent createInverseFieldComponent ( final RelationshipElement field) { return new ValidationComponent () { public void validate () throws ModelValidationException { Model model = getModel(); RelationshipElement inverse = field.getInverseRelationship(model); RelationshipElement inverseInverse = ((inverse != null) ? inverse.getInverseRelationship(model) : null); if ((inverse != null) && (!field.equals(inverseInverse) || (inverseInverse == null))) { String fieldName = field.getName(); throw new ModelValidationException( model.getField(getClassName(), fieldName), I18NHelper.getMessage(getMessages(), "util.validation.inverse_field_invalid", //NOI18N new Object[]{fieldName, inverse.getName()})); } } }; } /** Create a validation component which can check whether the inverse of * the relationship belongs to the related class (type or element class * depending on cardinality). * @param field the relationship whose inverse relationship is being checked * @return the validation component */ protected ValidationComponent createRelatedClassMatchesComponent ( final RelationshipElement field) { return new ValidationComponent () { public void validate () throws ModelValidationException { String inverseName = field.getInverseRelationshipName(); if (!StringHelper.isEmpty(inverseName)) { Model model = getModel(); RelationshipElement inverse = field.getInverseRelationship(model); if (inverse == null) // no such field in that related class { String relatedClass = getRelatedClass(field); String fieldName = field.getName(); String key = ((relatedClass != null) ? "util.validation.related_class_mismatch" : //NOI18N "util.validation.related_class_not_found");//NOI18N Object[] args = ((relatedClass != null) ? new Object[]{fieldName, inverseName, relatedClass} : new Object[]{fieldName, inverseName}); throw new ModelValidationException( model.getField(getClassName(), fieldName), I18NHelper.getMessage(getMessages(), key, args)); } } } }; } /** Create a validation component which can check whether the mapping of * the relationship and the mapping of its inverse are inverses of each * other. * @param field the relationship whose inverse relationship is being checked * @return the validation component */ protected ValidationComponent createInverseMappingComponent ( final RelationshipElement field) { return new ValidationComponent () { public void validate () throws ModelValidationException { Model model = getModel(); RelationshipElement inverse = field.getInverseRelationship(model); if ((inverse != null) && !isInverseMapping(field, inverse)) { String fieldName = field.getName(); throw new ModelValidationException( model.getField(getClassName(), fieldName), I18NHelper.getMessage(getMessages(), "util.validation.inverse_mapping_mismatch", //NOI18N new Object[]{fieldName, inverse.getName()})); } } private boolean hasMappingRows (MappingRelationshipElement field2) { if (field2 != null) { ArrayList columns = field2.getColumns(); return ((columns != null) && !columns.isEmpty()); } return false; } private boolean isInverseMapping (RelationshipElement jdoField1, RelationshipElement jdoField2) { MappingRelationshipElement field1 = getMappingRelationship(jdoField1); MappingRelationshipElement field2 = getMappingRelationship(jdoField2); boolean field1HasMapping = hasMappingRows(field1); boolean field2HasMapping = hasMappingRows(field2); // if both have rows, they must be exact inverses if (field1HasMapping && field2HasMapping) { boolean field1IsJoin = isJoin(field1); if (field1IsJoin == isJoin(field2)) { ArrayList pairs1 = field1.getColumns(); ArrayList pairs2 = field2.getColumns(); return ((!field1IsJoin) ? isInverse(pairs1, pairs2) : (isInverse(pairs1, field2.getAssociatedColumns()) && isInverse(field1.getAssociatedColumns(), pairs2))); } return false; } // if neither have rows that's fine return (field1HasMapping == field2HasMapping); } private boolean isInverse (ArrayList pairs1, ArrayList pairs2) { int i, size1 = pairs1.size(), size2 = pairs2.size(); if (size1 == size2) { for (i = 0; i < size1; i++) { String nextPair = (String)pairs1.get(i); String inversePair = (String)pairs2.get(i); int semicolonIndex1 = nextPair.indexOf(';'); int semicolonIndex2 = inversePair.indexOf(';'); if (((semicolonIndex1 == -1) || (semicolonIndex2 == -1)) || (!nextPair.substring(0, semicolonIndex1).equals( inversePair.substring(semicolonIndex2 + 1)) || !nextPair.substring(semicolonIndex1 + 1).equals( inversePair.substring(0, semicolonIndex2)))) { return false; } } return true; } return false; } }; } /** Create a validation component which can check whether the field is * part of a managed (multiple fields to same column) group and in an * illegal fetch group. If the field is in one of these groups, it is * not allowed to be in the default fetch group. * @param field the field whose fetch group is being checked * @return the validation component */ protected ValidationComponent createFieldDefaultFetchGroupComponent ( final MappingFieldElement field) { return new ValidationComponent () { public void validate () throws ModelValidationException { if (field != null) { String fieldName = field.getName(); PersistenceClassElement persistenceClass = getPersistenceClass(getClassName()); PersistenceFieldElement pElement = ((persistenceClass != null) ? persistenceClass.getField(fieldName) : null); if ((pElement != null) && !pElement.isKey() && (MappingFieldElement.GROUP_DEFAULT == field.getFetchGroup())) { MappingClassElement mappingClass = field.getDeclaringClass(); boolean isVersionField = ((MappingClassElement.VERSION_CONSISTENCY == mappingClass.getConsistencyLevel()) && field.isVersion()); Iterator iterator = mappingClass.getFields().iterator(); String exceptionKey = (!isVersionField ? "util.validation.field_fetch_group_invalid"://NOI18N "util.validation.version_field_column_invalid");//NOI18N /* rules: * primitive, primitive -> error if exact match of * columns * primitive, relationship OR * relationship, primitive -> error if non-collection * relationship and none of the relationship's * columns are PK columns and any are present in * the primitive's list * relationship, relationship -> error if exact * match of mapping (local, join, associated), * but order is not important */ while (iterator.hasNext()) { MappingFieldElement testField = (MappingFieldElement)iterator.next(); if (isManaged(field, testField) || isManaged(testField, field)) { throw constructFieldException( fieldName, exceptionKey); } else if (!testField.equals(field) && isExactMatch( field, testField)) { throw constructFieldException( fieldName, exceptionKey); } } } } } private boolean isManaged (MappingFieldElement primField, MappingFieldElement relField) { String className = getClassName(); if (!isRelationship(primField) && isRelationship(relField) && !isCollection(className, relField.getName())) { ArrayList columns = primField.getColumns(); Iterator iterator = relField.getColumns().iterator(); String databaseRoot = getSchemaForClass(className); while (iterator.hasNext()) { if (!testColumn(getLocalColumn((String)iterator.next(), databaseRoot), columns)) { return true; } } } return false; } private boolean testColumn (ColumnElement column, ArrayList masterList) { if ((column != null) && !isPrimaryKeyColumn(column)) { return !masterList.contains(NameUtil. getRelativeMemberName(column.getName().getFullName())); } return true; } private ColumnElement getLocalColumn (String pairName, String databaseRoot) { ColumnPairElement pair = getPair(pairName, databaseRoot); return ((pair != null) ? pair.getLocalColumn() : null); } private boolean isPrimaryKeyColumn (ColumnElement column) { if (column != null) { KeyElement key = column.getDeclaringTable().getPrimaryKey(); return ((key != null) && (key.getColumn(column.getName()) != null)); } return false; } private boolean isExactMatch (ArrayList columns1, ArrayList columns2) { int count = columns1.size(); if ((count > 0) && (count == columns2.size())) return getDifference(columns1, columns2).isEmpty(); return false; } private boolean isExactMatch (MappingFieldElement field1, MappingFieldElement field2) { boolean field1IsRel = isRelationship(field1); boolean match = false; // both primitives, or both relationships if (field1IsRel == isRelationship(field2)) { match = isExactMatch(field1.getColumns(), field2.getColumns()); if (match && field1IsRel) { MappingRelationshipElement rel1 = (MappingRelationshipElement)field1; MappingRelationshipElement rel2 = (MappingRelationshipElement)field2; boolean field1IsJoin = isJoin(rel1); // both join relationships or both direct if (field1IsJoin == isJoin(rel2)) { if (field1IsJoin) { match = isExactMatch( rel1.getAssociatedColumns(), rel2.getAssociatedColumns()); } } else match = false; } } return match; } }; } /** Create a validation component which can check whether the schema of * the related class matches that of the class we are checking. * @param relatedClass the class whose schema is being checked * @param relatedField the relationship field whose schema is being * compared * @return the validation component */ protected ValidationComponent createRelatedSchemaMatchesComponent ( final String relatedClass, final PersistenceFieldElement relatedField) { return new ValidationComponent () { public void validate () throws ModelValidationException { if (relatedClass != null) { String className = getClassName(); String mySchema = getSchemaForClass(className); String relatedSchema = getSchemaForClass(relatedClass); if ((mySchema != null) && (relatedSchema != null) && !(relatedSchema.equals(mySchema))) { String fieldName = relatedField.getName(); throw new ModelValidationException( getModel().getField(className, fieldName), I18NHelper.getMessage(getMessages(), "util.validation.schema_mismatch", //NOI18N new Object[]{className, relatedClass, fieldName})); } } } }; } /** Create a validation component which can check whether any of * the supplied tables of the related class (which includes primary * and secondary tables) contains the table of the column stored in * the relationship definition. * @param relatedClass the class whose table is being checked * @param relatedField the relationship field whose table is being compared * @param tableNames the list of names of the tables we expect the * column to match * @param pairName the name of the pair whose reference column is to * be checked * @return the validation component */ protected ValidationComponent createRelatedTableMatchesComponent ( final String relatedClass, final PersistenceFieldElement relatedField, final List tableNames, final String pairName) { return new ValidationComponent () { public void validate () throws ModelValidationException { ColumnPairElement pair = getPair(pairName, getSchemaForClass(relatedClass)); if (pair != null) { ColumnElement column = pair.getReferencedColumn(); if (!matchesTable(tableNames, column)) { String fieldName = relatedField.getName(); throw new ModelValidationException( getModel().getField(getClassName(), fieldName), I18NHelper.getMessage(getMessages(), getKey( "util.validation.table_mismatch", //NOI18N relatedField), new Object[]{column.getName().getFullName(), fieldName, relatedClass})); } } } }; } /** Create a validation component which can check whether the schema of * the given class exists. * @param className the class whose mapped schema's existence is * being checked * @return the validation component */ protected ValidationComponent createSchemaExistenceComponent ( final String className) { return createSchemaExistenceComponent(className, null); } /** Create a validation component which can check whether the schema of * the given class exists. * @param className the class whose mapped schema's existence is * being checked * @param relatedField the relationship field whose class' * mapped schema is being checked, may be null in which * case we are probably checking the same class as the validator is * checking overall * @return the validation component */ protected ValidationComponent createSchemaExistenceComponent ( final String className, final PersistenceFieldElement relatedField) { return new ValidationComponent () { public void validate () throws ModelValidationException { String schemaName = getSchemaForClass(className); if ((schemaName != null) && (SchemaElement.forName(schemaName) == null)) { Object[] args = (relatedField == null) ? new Object[]{schemaName, className} : new Object[]{schemaName, className, relatedField}; throw new ModelValidationException( ModelValidationException.WARNING, getOffendingObject(relatedField), I18NHelper.getMessage(getMessages(), getKey( "util.validation.schema_not_found", //NOI18N relatedField), args)); } } }; } /** Create a validation component which can check whether the * class is mapped to tables even though the schema is null or the * class is mapped to a primary table without a primary key. * @param primaryTable the primary table for the class * @return the validation component */ protected ValidationComponent createPrimaryTableComponent ( final MappingTableElement primaryTable) { return new ValidationComponent () { public void validate () throws ModelValidationException { if (primaryTable != null) { String className = getClassName(); String schemaName = getSchemaForClass(className); if (schemaName == null) { throw constructClassException(className, null, "util.validation.schema_not_set"); //NOI18N } else { String tableName = primaryTable.getName(); TableElement table = getTable(tableName, schemaName); if ((table != null) && (table.getPrimaryKey() == null)) { throw new ModelValidationException( getOffendingObject(null), I18NHelper.getMessage(getMessages(), "util.validation.table_no_primarykey", //NOI18N new Object[]{tableName, className})); } } } } }; } /** Create a validation component which can check whether the given table * exists. * @param tableName the table whose existence is being checked * @return the validation component */ protected ValidationComponent createTableExistenceComponent ( final String tableName) { return createTableExistenceComponent(tableName, null); } /** Create a validation component which can check whether the given table * exists. * @param tableName the table whose existence is being checked * @param relatedField the relationship field whose class' * table is being checked, may be null in which * case we are probably checking the same class as the validator is * checking overall * @return the validation component */ protected ValidationComponent createTableExistenceComponent ( final String tableName, final PersistenceFieldElement relatedField) { return new ValidationComponent () { public void validate () throws ModelValidationException { if (tableName != null) { String className = getClassName(); boolean noRelated = (relatedField == null); TableElement table = getTable(tableName, getSchemaForClass((noRelated ? className : getRelatedClass(relatedField)))); if (table == null) { Object[] args = noRelated ? new Object[]{tableName, className} : new Object[]{tableName, relatedField}; throw new ModelValidationException( ModelValidationException.WARNING, getOffendingObject(relatedField), I18NHelper.getMessage(getMessages(), getKey( "util.validation.table_not_found", //NOI18N relatedField), args)); } } } }; } /** Create a validation component which can check whether the given column * exists. * @param columnName the column whose existence is being checked * @return the validation component */ protected ValidationComponent createColumnExistenceComponent ( final String columnName) { return createColumnExistenceComponent(columnName, null); } /** Create a validation component which can check whether the given * column or column pair exists. * @param columnName the column or pair whose existence is being checked * @param relatedField the field whose class' column is being checked, * may be null in which case we are probably checking the * same secondary table setup * @return the validation component */ protected ValidationComponent createColumnExistenceComponent ( final String columnName, final MappingFieldElement relatedField) { return new ValidationComponent () { public void validate () throws ModelValidationException { if (columnName != null) { String className = getClassName(); String absoluteName = NameUtil.getAbsoluteMemberName( getSchemaForClass(className), columnName); TableElement table = TableElement.forName( NameUtil.getTableName(absoluteName)); boolean foundTable = (table != null); DBMemberElement columnElement = ((foundTable) ? table.getMember(DBIdentifier.create(absoluteName)) : null); boolean noRelated = (relatedField == null); if (foundTable) { boolean isRelationship = (!noRelated && isRelationship(relatedField)); boolean noColumn = (columnElement == null); if (!isRelationship && noColumn) { Object[] args = (noRelated) ? new Object[]{columnName, className} : new Object[]{columnName, relatedField, className}; throw new ModelValidationException( ModelValidationException.WARNING, getOffendingObject(relatedField), I18NHelper.getMessage(getMessages(), getKey( "util.validation.column_not_found", //NOI18N relatedField), args)); } else if (isRelationship && (noColumn || !isPairComplete(columnElement))) { throw new ModelValidationException( ModelValidationException.WARNING, getOffendingObject(relatedField), I18NHelper.getMessage(getMessages(), "util.validation.column_invalid", //NOI18N new Object[]{columnName, relatedField, className})); } } } } private boolean isPairComplete (DBMemberElement member) { return ((member instanceof ColumnPairElement) && (((ColumnPairElement)member).getLocalColumn() != null) && (((ColumnPairElement)member).getReferencedColumn() != null)); } }; } /** Create a validation component which can check whether the field is * one of a set mapped to overlapping columns * @param field the field whose column mapping is being checked * @return the validation component */ protected ValidationComponent createColumnOverlapComponent ( final MappingFieldElement field) { return new ValidationComponent () { public void validate () throws ModelValidationException { MappingClassElement mappingClass = field.getDeclaringClass(); Iterator iterator = mappingClass.getFields().iterator(); ArrayList myColumns = field.getColumns(); while (iterator.hasNext()) { MappingFieldElement testField = (MappingFieldElement)iterator.next(); if (!testField.equals(field) && !isRelationship(testField) && isPartialMatch(myColumns, testField.getColumns())) { String fieldName = field.getName(); throw new ModelValidationException(getModel().getField( getClassName(), fieldName), I18NHelper.getMessage(getMessages(), "util.validation.field_mapping_invalid", //NOI18N new Object[]{fieldName, testField.getName()})); } } } private boolean isPartialMatch (ArrayList columns1, ArrayList columns2) { int count = columns1.size(); if (count > 0) { ArrayList difference = getDifference(columns1, columns2); return (!difference.isEmpty() && (columns2.size() != difference.size())); } return false; } }; } /** Create a validation component which can check whether the key class * of the persistence capable class is valid. This includes: *
    *
  • The key class must be public. *
  • The key class must implement Serializable. *
  • If the key class is an inner class, it must be static. *
  • The key class must have a public constructor, which might be * the default constructor or a no-arg constructor. *
  • The field types of all non-static fields in the key class must be * of valid types. *
  • All serializable non-static fields in the key class must be public. *
  • The names of the non-static fields in the key class must include the * names of the primary key fields in the JDO class, and the types of the * common fields must be identical *
  • The key class must redefine equals and hashCode. *
*/ protected ValidationComponent createKeyClassComponent ( final String className) { return new ValidationComponent () { /** The class element of the key class */ private Object keyClass; /** The fully qualified name of the key class */ private String keyClassName; public void validate () throws ModelValidationException { // checks the key class name keyClassName = validateKeyClassName(className); // initilialize keyClass field keyClass = getModel().getClass(keyClassName, getClassLoader()); validateClass(); validateConstructor(); validateFields(); validateMethods(); } /** Helper method validating the key class itself: * public, serializable, static. */ private void validateClass () throws ModelValidationException { Model model = getModel(); int modifiers = model.getModifiersForClass(keyClassName); boolean hasKeyClassName = !StringHelper.isEmpty(keyClassName); boolean isInnerClass = (hasKeyClassName && (keyClassName.indexOf('$') != -1)); String pcClassName = getClassName(); // check for key class existence if (keyClass == null) { throw new ModelValidationException( ModelValidationException.WARNING, model.getClass(pcClassName), I18NHelper.getMessage(getMessages(), "util.validation.key_class_missing", //NOI18N keyClassName, pcClassName)); } // check for public class modifier if (!Modifier.isPublic(modifiers)) { throw new ModelValidationException(keyClass, I18NHelper.getMessage(getMessages(), "util.validation.key_class_public", //NOI18N keyClassName, pcClassName)); } // check for Serializable /* This check is disabled because of Boston backward compatibility. In Boston there was no requirement for a key class being serializable, thus key classes from pc classes mapped with boston are not serializable. if (!model.implementsInterface(keyClass, "java.io.Serializable")) //NOI18N { throw new ModelValidationException(keyClass, I18NHelper.getMessage(getMessages(), "util.validation.key_class_serializable", //NOI18N keyClassName, pcClassName)); } */ // if inner class it must be static if (isInnerClass && !Modifier.isStatic(modifiers)) { throw new ModelValidationException(keyClass, I18NHelper.getMessage(getMessages(), "util.validation.key_class_static", //NOI18N keyClassName, pcClassName)); } } /** Helper method validating the fields of the key class. */ private void validateFields () throws ModelValidationException { String pcClassName = getClassName(); Model model = getModel(); // check for valid typed public non-static fields List keyClassFieldNames = model.getAllFields(keyClassName); Map keyFields = getKeyFields(); for (Iterator i = keyClassFieldNames.iterator(); i.hasNext();) { String keyClassFieldName = (String)i.next(); Object keyClassField = getKeyClassField(keyClassName, keyClassFieldName); int keyClassFieldModifiers = model.getModifiers(keyClassField); String keyClassFieldType = model.getType(keyClassField); Object keyField = keyFields.get(keyClassFieldName); if (Modifier.isStatic(keyClassFieldModifiers)) // we are not interested in static fields continue; if (!model.isValidKeyType(keyClassName, keyClassFieldName)) { throw new ModelValidationException(keyClassField, I18NHelper.getMessage(getMessages(), "util.validation.key_field_type_invalid", //NOI18N keyClassFieldName, keyClassName)); } if (!Modifier.isPublic(keyClassFieldModifiers)) { throw new ModelValidationException(keyClassField, I18NHelper.getMessage(getMessages(), "util.validation.key_field_public", //NOI18N keyClassFieldName, keyClassName)); } if (keyField == null) continue; if (!keyClassFieldType.equals(model.getType(keyField))) { throw new ModelValidationException(keyClassField, I18NHelper.getMessage(getMessages(), "util.validation.key_field_type_mismatch", //NOI18N keyClassFieldName, keyClassName, pcClassName)); } // remove handled keyField from the list of keyFields keyFields.remove(keyClassFieldName); } // check whether there are any unhandled key fields if (!keyFields.isEmpty()) { Object pcClass = model.getClass(pcClassName); String fieldNames = StringHelper.arrayToSeparatedList( new ArrayList(keyFields.keySet())); throw new ModelValidationException(pcClass, I18NHelper.getMessage(getMessages(), "util.validation.key_field_missing", //NOI18N pcClassName, keyClassName, fieldNames)); } } /** Helper method validating the key class constructors. */ private void validateConstructor () throws ModelValidationException { // no constructor or no arg constructor Model model = getModel(); boolean hasConstr = model.hasConstructor(keyClassName); Object noArgConstr = model.getConstructor(keyClassName, Model.NO_ARGS); int modifiers = model.getModifiers(noArgConstr); if (hasConstr && ((noArgConstr == null) || !Modifier.isPublic(modifiers))) { throw new ModelValidationException(keyClass, I18NHelper.getMessage(getMessages(), "util.validation.key_class_constructor", //NOI18N keyClassName, getClassName())); } } /** Helper method validating the key class methods. */ private void validateMethods () throws ModelValidationException { Model model = getModel(); Object equalsMethod = getNonObjectMethod(keyClassName, "equals", Model.getEqualsArgs()); //NOI18N Object hashCodeMethod = getNonObjectMethod(keyClassName, "hashCode", Model.NO_ARGS); //NOI18N // check equals method if (!matchesMethod(equalsMethod, Modifier.PUBLIC, 0, "boolean")) //NOI18N { throw new ModelValidationException(keyClass, I18NHelper.getMessage(getMessages(), "util.validation.key_class_equals", //NOI18N keyClassName, getClassName())); } // check hashCode method if (!matchesMethod(hashCodeMethod, Modifier.PUBLIC, 0, "int")) //NOI18N { throw new ModelValidationException(keyClass, I18NHelper.getMessage(getMessages(), "util.validation.key_class_hashcode", //NOI18N keyClassName, getClassName())); } } /** Helper method validating the name of the key class. */ private String validateKeyClassName (String keyClassName) throws ModelValidationException { String pcClassName = getClassName(); Model model = getModel(); boolean hasKeyClassName = !StringHelper.isEmpty(keyClassName); boolean hasPrefix; String nameSuffix; boolean isOIDNameSuffix; // check for existence of key class name if (!hasKeyClassName) { throw new ModelValidationException( ModelValidationException.WARNING, model.getClass(pcClassName), I18NHelper.getMessage(getMessages(), "util.validation.key_class_unset", //NOI18N pcClassName)); } keyClassName = keyClassName.trim(); hasPrefix = keyClassName.startsWith(pcClassName); nameSuffix = (hasPrefix ? keyClassName.substring(pcClassName.length()) : keyClassName); isOIDNameSuffix = (nameSuffix.equalsIgnoreCase(".OID") || // NOI18N nameSuffix.equalsIgnoreCase("$OID")); // NOI18N if (!hasPrefix || (!nameSuffix.equalsIgnoreCase("Key") && // NOI18N !isOIDNameSuffix)) { Object pcClass = getModel().getClass(pcClassName); throw new ModelValidationException(pcClass, I18NHelper.getMessage(getMessages(), "util.validation.key_class_invalid", //NOI18N keyClassName, pcClassName)); } if (isOIDNameSuffix) { StringBuilder buf = new StringBuilder(keyClassName); buf.setCharAt(keyClassName.length() - 4, '$'); return buf.toString(); } return keyClassName; } // helper method which returns a field object from the // given class or one of its superclasses private Object getKeyClassField (String keyClassName, String keyClassFieldName) { Model model = getModel(); Object keyClassField = model.getField(keyClassName, keyClassFieldName); if (keyClassField == null) // this is an inherited field { keyClassField = model.getInheritedField( keyClassName, keyClassFieldName); } return keyClassField; } /** Helper method returning the key fields of the pc class as a map. */ private Map getKeyFields () { Model model = getModel(); String pcClassName = getClassName(); PersistenceClassElement pce = model.getPersistenceClass(pcClassName); PersistenceFieldElement[] fields = pce.getFields(); Map keyFields = new HashMap(); if (fields != null) { for (int i = 0; i < fields.length; i++) { PersistenceFieldElement pfe = fields[i]; if (pfe.isKey()) { String name = pfe.getName(); keyFields.put(name, model.getField(pcClassName, name)); } } } return keyFields; } // helper method which returns a method object from the // given class or one of its superclasses provided it // is not java.lang.Object private Object getNonObjectMethod (String className, String methodName, String[] argTypeNames) { Model model = getModel(); Object method = model.getMethod(className, methodName, argTypeNames); if (method == null) // look for an inherited method { method = model.getInheritedMethod( className, methodName, argTypeNames); if ((method != null) && model.getDeclaringClass(method). equals("java.lang.Object")) // NOI18N { method = null; } } return method; } }; } /** Create a validation component which can check that the persistence * capable class implement methods readObject and writeObject, if the class * implements the intreface java.io.Serializable * @param className the class whose methods are checked * @return the validation component */ protected ValidationComponent createSerializableClassComponent ( final String className) { return new ValidationComponent () { public void validate () throws ModelValidationException { Model model = getModel(); Object pcClass = null; if (className == null) return; pcClass = model.getClass(className); if (pcClass == null) return; if (model.implementsInterface(pcClass, "java.io.Serializable")) //NOI18N { // check readObject method Object readMethod = model.getMethod(className, "readObject", Model.getReadObjectArgs()); //NOI18N if (!matchesMethod(readMethod, Modifier.PRIVATE, Modifier.SYNCHRONIZED, "void")) // NOI18N { throw new ModelValidationException(pcClass, I18NHelper.getMessage(getMessages(), "util.validation.class_readobject", //NOI18N className)); } // check writeObject method Object writeMethod = model.getMethod(className, "writeObject", Model.getWriteObjectArgs()); //NOI18N if (!matchesMethod(writeMethod, Modifier.PRIVATE, Modifier.SYNCHRONIZED, "void")) // NOI18N { throw new ModelValidationException(pcClass, I18NHelper.getMessage(getMessages(), "util.validation.class_writeobject", //NOI18N className)); } } } }; } /** Create a validation component which can check whether the class is * unmapped. * @param persistenceClass the class whose mapping is being checked * @return the validation component */ protected ValidationComponent createClassMappingComponent ( final PersistenceClassElement persistenceClass) { return new ValidationComponent () { public void validate () throws ModelValidationException { PersistenceFieldElement[] fields = persistenceClass.getFields(); String className = getClassName(); if ((fields == null) || fields.length == 0) { throw constructClassException( ModelValidationException.WARNING, className, null, "util.validation.class_no_fields"); //NOI18N } else // has fields, check for primary table { MappingClassElement mappingClass = getMappingClass(className); if ((mappingClass == null) || (mappingClass.getTables().size() == 0)) { throw constructClassException( ModelValidationException.WARNING, className, null, "util.validation.class_not_mapped"); //NOI18N } } } }; } /** Create a validation component which can check whether the class * contains field mappings for all primary key columns. * @param persistenceClass the class whose mapping is being checked * @return the validation component */ protected ValidationComponent createKeyColumnMappingComponent ( final PersistenceClassElement persistenceClass) { return new ValidationComponent () { public void validate () throws ModelValidationException { String className = getClassName(); MappingClassElement mappingClass = getMappingClass(className); if (mappingClass != null) { List tables = mappingClass.getTables(); if (tables.size() > 0) { String tableName = ((MappingTableElement)tables.get(0)).getName(); TableElement table = getTable(tableName, getSchemaForClass(className)); List columns = getUnmappedColumnNames( ((table != null) ? table.getPrimaryKey() : null), mappingClass); if ((columns != null) && (columns.size() > 0)) { throw new ModelValidationException( ModelValidationException.WARNING, getOffendingObject(null), I18NHelper.getMessage(getMessages(), "util.validation.class_key_column_missing", //NOI18N className, tableName, StringHelper.arrayToSeparatedList(columns))); } } } } private List getUnmappedColumnNames (KeyElement primaryKey, MappingClassElement mappingClass) { List unmappedColumns = null; if (primaryKey != null) // check if primary table has a pk { ColumnElement[] columns = primaryKey.getColumns(); int count = ((columns != null) ? columns.length : 0); // all columns in the pk should be mapped to key fields if (count > 0) { List mappingFields = mappingClass.getFields(); Iterator iterator = mappingFields.iterator(); unmappedColumns = getRelativeColumnNames(columns); while (iterator.hasNext()) { MappingFieldElement field = (MappingFieldElement)iterator.next(); if (isKeyField(field)) unmappedColumns.removeAll(field.getColumns()); } } } return unmappedColumns; } private List getRelativeColumnNames (ColumnElement[] columns) { int i, count = ((columns != null) ? columns.length : 0); List columnNames = new ArrayList(count); for (i = 0; i < count; i++) { columnNames.add(NameUtil.getRelativeMemberName( columns[i].getName().getFullName())); } return columnNames; } private boolean isKeyField (MappingFieldElement field) { PersistenceFieldElement persistenceField = persistenceClass.getField(field.getName()); return ((persistenceField != null) && persistenceField.isKey()); } }; } //========== Convenience methods for exception construction ============ /** Computes the offending object to be used in the construction of a * ModelValidationException as follows: if a non-null field is supplied, * the corresponding org.openide.src.FieldElement is returned, otherwise, * corresponding org.openide.src.ClassElement is returned. * @param field the field object which caused the problem - may be * null * @return the offending object */ private Object getOffendingObject (Object field) { return ((field == null) ? getModel().getClass(getClassName(), getClassLoader()) : getModel().getField(getClassName(), field.toString())); } /** Computes the key for the i18n string to be used in the construction * of a ModelValidationException as follows: if a non-null field is * supplied, "_related" is appending to the supplied key base, otherwise, * the key base is returned as is. * @param keyBase the base key to be used for the i18n string * @param field the field object which caused the problem - may be * null * @return the key */ private String getKey (String keyBase, Object field) { return ((field == null) ? keyBase : (keyBase + "_related")); //NOI18N } /** Computes the arguments for the i18n string to be used in the * construction of a ModelValidationException as follows: if a * non-null field is supplied, an array containing the supplied className * and field is returned, otherwise, an array containing only the supplied * className is returned. * @param className the name of the class which caused the problem * @param field the field object which caused the problem - may be * null * @return the argument array */ private Object[] getArguments (String className, Object field) { return ((field == null) ? new Object[]{className} : new Object[]{className, field}); } /** Constructs a ModelValidationException for class validation tests * using the supplied class name, related field, and key base. * @param className the name of the class which caused the problem * @param field the field object which caused the problem - may be * null * @param keyBase the base key to be used for the i18n string * @return the ModelValidationException * @see #getOffendingObject * @see #getKey * @see #getArguments * @see #constructFieldException */ private ModelValidationException constructClassException (String className, Object relatedField, String keyBase) { return constructClassException(ModelValidationException.ERROR, className, relatedField, keyBase); } /** Constructs a ModelValidationException for class validation tests * using the supplied class name, related field, and key base. * @param errorType the type of error -- one of * {@link ModelValidationException#ERROR} or * {@link ModelValidationException#WARNING}. * @param className the name of the class which caused the problem * @param field the field object which caused the problem - may be * null * @param keyBase the base key to be used for the i18n string * @return the ModelValidationException * @see #getOffendingObject * @see #getKey * @see #getArguments * @see #constructFieldException */ private ModelValidationException constructClassException (int errorType, String className, Object relatedField, String keyBase) { return new ModelValidationException(errorType, getOffendingObject(relatedField), I18NHelper.getMessage( getMessages(), getKey(keyBase, relatedField), getArguments(className, relatedField))); } /** Constructs a ModelValidationException for field validation tests * using the supplied field name and key. * @param fieldName the name of the field which caused the problem * @param keyBase the base key to be used for the i18n string * @return the ModelValidationException * @see #constructClassException */ private ModelValidationException constructFieldException (String fieldName, String key) { return constructFieldException(ModelValidationException.ERROR, fieldName, key); } /** Constructs a ModelValidationException for field validation tests * using the supplied field name and key. * @param errorType the type of error -- one of * {@link ModelValidationException#ERROR} or * {@link ModelValidationException#WARNING}. * @param fieldName the name of the field which caused the problem * @param keyBase the base key to be used for the i18n string * @return the ModelValidationException * @see #constructClassException */ private ModelValidationException constructFieldException (int errorType, String fieldName, String key) { return new ModelValidationException(errorType, getModel().getField(getClassName(), fieldName), I18NHelper.getMessage(getMessages(), key, fieldName)); } //=============== Misc. private convenience methods ================ /** Checks whether the specified method element exists and if so whether it * has the expected modifiers and the expected return type. * @param method the method element to be checked * @param expectedModifiers the modifiers the method should have * @param optionalModifiers additional modifiers the method might have * @param expectedReturnType the return type the method should have * @return true if the method matches, * false otherwise. */ private boolean matchesMethod (final Object method, final int expectedModifiers, final int optionalModifiers, final String expectedReturnType) { boolean matches = false; if (method != null) { Model model = getModel(); int modifiers = model.getModifiers(method); matches = (((modifiers == expectedModifiers) || (modifiers == (expectedModifiers | optionalModifiers))) && expectedReturnType.equals(model.getType(method))); } return matches; } /** Check if the table of the column matches one of the list of tables. * @param tableNames A list of table names in which to check for a match * @param column A ColumnElement object to be checked * @return true if the column belongs to a table found * in the supplied list of table names, false otherwise */ private boolean matchesTable (List tableNames, ColumnElement column) { return ((column == null) ? true : tableNames.contains( column.getDeclaringTable().getName().getName())); } private boolean isRelationship (Object field) { return ((field instanceof RelationshipElement) || (field instanceof MappingRelationshipElement)); } private boolean shouldBeRelationship (PersistenceFieldElement field) { Model model = getModel(); String fieldType = model.getFieldType(getClassName(), field.getName()); return (isPersistent(fieldType) || model.isCollection(fieldType)); } private boolean isLegalRelationship (PersistenceFieldElement field) { return (isRelationship(field) ? shouldBeRelationship(field) : false); } private boolean isCollection (String className, String fieldName) { Model model = getModel(); return model.isCollection(model.getFieldType(className, fieldName)); } private String getRelatedClass (PersistenceFieldElement field) { if (isLegalRelationship(field)) return getModel().getRelatedClass((RelationshipElement)field); return null; } private String getSchemaForClass (String className) { MappingClassElement mappingClass = getMappingClass(className); String schema = ((mappingClass != null) ? mappingClass.getDatabaseRoot() : null); return (StringHelper.isEmpty(schema) ? null : schema.trim()); } private MappingRelationshipElement getMappingRelationship ( RelationshipElement jdoElement) { MappingRelationshipElement mappingElement = null; if (jdoElement != null) { MappingClassElement mappingClass = getMappingClass( jdoElement.getDeclaringClass().getName()); if (mappingClass != null) { MappingFieldElement fieldElement = mappingClass.getField(jdoElement.getName()); if (isRelationship(fieldElement)) mappingElement = (MappingRelationshipElement)fieldElement; } } return mappingElement; } private boolean isJoin (MappingRelationshipElement field) { if (field != null) { ArrayList columns = field.getAssociatedColumns(); return ((columns != null) && !columns.isEmpty()); } return false; } private MappingReferenceKeyElement findReferenceKey ( MappingTableElement primaryTable, MappingTableElement secondaryTable) { if ((primaryTable != null) && (secondaryTable != null)) { Iterator iterator = primaryTable.getReferencingKeys().iterator(); while (iterator.hasNext()) { MappingReferenceKeyElement testKey = (MappingReferenceKeyElement)iterator.next(); if (testKey.getTable().equals(secondaryTable)) return testKey; } } return null; } private TableElement getTable (String tableName, String databaseRoot) { String absoluteName = NameUtil.getAbsoluteTableName(databaseRoot, tableName); return TableElement.forName(absoluteName); } private ColumnPairElement getPair (String pairName, String databaseRoot) { String absoluteName = NameUtil.getAbsoluteMemberName( databaseRoot, pairName); TableElement tableElement = TableElement.forName( NameUtil.getTableName(absoluteName)); DBMemberElement pair = ((tableElement == null) ? null : tableElement.getMember(DBIdentifier.create(absoluteName))); return ((pair instanceof ColumnPairElement) ? ((ColumnPairElement)pair) : null); } private ArrayList getDifference (ArrayList columns1, ArrayList columns2) { ArrayList differenceColumns = new ArrayList(columns2); differenceColumns.removeAll(columns1); return differenceColumns; } /** * Convenience method to call Model.getMappingClass. */ private MappingClassElement getMappingClass (String className) { return getModel().getMappingClass(className, getClassLoader()); } /** * Convenience method to call Model.getPersistenceClass. */ private PersistenceClassElement getPersistenceClass (String className) { return getModel().getPersistenceClass(className, getClassLoader()); } /** * Convenience method to call Model.isPersistent */ private boolean isPersistent (String className) { return getModel().isPersistent(className, getClassLoader()); } /** * Convenience method to call Model.isPersistentAllowed */ private boolean isPersistentAllowed (String className, String fieldName) { return getModel().isPersistentAllowed(className, getClassLoader(), fieldName); } // ================== Validation component support ================= /** Abstraction of component tests for validation. */ static abstract class ValidationComponent { /** Constructs a new ValidationComponent */ public ValidationComponent () { } /** Method which validates this component * @exception ModelValidationException when the validation fails. */ public abstract void validate () throws ModelValidationException; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy