Add PolicyIdentOptVersion

Added additional PolicyIdentXxx classes.  Added PdpDeployPolicies, which
makes use of it, thus eliminating the need for PdpPolicies, which will be
deleted once the PAP has been modified to use the new code.
Added Validated class to facilitate field validation.
Added utility methods to Validated class.
Use new validator methods in PolicyIdentXxx classes.
Use addError() method in validator class.
Use parameter types instead of "?".
Use static Validator instead of local.
Get "result" from each call to a validateXxx() method.
Derived PolicyIdentOptVersion from PfConceptKey.
Moved PolicyIdent classes to models-pdp.
Added PolicyIdent classes to models-pap.
Also removed copy constructors from classes in models-pap, as those
are plain POJOs that will not be copied.  Copy constructors will be
added to new classes that will be added to models-pdp in a separate
review.

Forgot to include the new Ident classes in models-pap.

Change-Id: I923132c464c7802ee3e9225685cde44f36c64620
Issue-ID: POLICY-1542
Signed-off-by: Jim Hahn <jrh3@att.com>
diff --git a/models-base/src/main/java/org/onap/policy/models/base/Validated.java b/models-base/src/main/java/org/onap/policy/models/base/Validated.java
new file mode 100644
index 0000000..7a0a837
--- /dev/null
+++ b/models-base/src/main/java/org/onap/policy/models/base/Validated.java
@@ -0,0 +1,253 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.base;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import lombok.NonNull;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.onap.policy.models.base.PfValidationResult.ValidationResult;
+
+/**
+ * Classes that can be validated. This can be used as a super class or as a stand-alone
+ * utility class.
+ */
+public class Validated {
+
+    /**
+     * Validates the fields of the object. The default method simply returns the result.
+     *
+     * @param result where to place the result
+     * @return the result
+     */
+    public PfValidationResult validate(@NonNull PfValidationResult result) {
+        return result;
+    }
+
+    /**
+     * Validates that a field value is not null.
+     *
+     * @param container the object that contains the field
+     * @param fieldName name of the field to be validated
+     * @param value value to be validated
+     * @param result where to place the result
+     * @return the result
+     */
+    public PfValidationResult validateNotNull(@NonNull Object container, @NonNull String fieldName, Object value,
+                    @NonNull PfValidationResult result) {
+
+        if (value == null) {
+            addError(container, fieldName, result, "null");
+        }
+
+        return result;
+    }
+
+    /**
+     * Validates that the name and version of a concept key do not have the null default
+     * values.
+     *
+     * @param value value to be validated
+     * @param result where to place the result
+     * @return the result
+     */
+    public PfValidationResult validateNotNull(@NonNull PfConceptKey value, @NonNull PfValidationResult result) {
+
+        if (PfConceptKey.NULL_KEY_NAME.equals(value.getName())) {
+            addError(value, "name", result, "null");
+        }
+
+        if (PfConceptKey.NULL_KEY_VERSION.equals(value.getVersion())) {
+            addError(value, "version", result, "null");
+        }
+
+        return result;
+    }
+
+    /**
+     * Validates the contents of a field, verifying that it matches a pattern, if it is
+     * non-null.
+     *
+     * @param container the object that contains the field
+     * @param fieldName name of the field to be validated
+     * @param value value to be validated
+     * @param pattern pattern used to validate the value
+     * @param result where to place the result
+     * @return the result
+     */
+    public PfValidationResult validateText(@NonNull Object container, @NonNull String fieldName, String value,
+                    @NonNull String pattern, @NonNull PfValidationResult result) {
+
+        if (value != null) {
+            addError(container, fieldName, result,
+                            Assertions.getStringParameterValidationMessage(fieldName, value, pattern));
+        }
+
+        return result;
+    }
+
+    /**
+     * Validates the contents of a property field, verifying that the keys ands values are
+     * non-null.
+     *
+     * @param container the object that contains the field
+     * @param fieldName name of the field to be validated
+     * @param properties properties to be validated
+     * @param resultIn where to place the result
+     * @return the result
+     */
+    public <T> PfValidationResult validatePropertiesNotNull(@NonNull Object container, @NonNull String fieldName,
+                    Map<String, T> properties, @NonNull PfValidationResult resultIn) {
+
+        PfValidationResult result = resultIn;
+
+        if (properties == null) {
+            return result;
+        }
+
+        for (Entry<String, T> ent : properties.entrySet()) {
+            String key = ent.getKey();
+            String keyName = fieldName + "." + key;
+            result = validateNotNull(container, keyName, key, result);
+
+            result = validateNotNull(container, keyName, ent.getValue(), result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Validates the items in a collection field are non-null.
+     *
+     * @param container the object that contains the field
+     * @param fieldName name of the field to be validated
+     * @param collection collection whose items are to be validated
+     * @param resultIn where to place the result
+     * @return the result
+     */
+    public <T> PfValidationResult validateCollectionNotNull(@NonNull Object container, @NonNull String fieldName,
+                    Collection<T> collection, @NonNull PfValidationResult resultIn) {
+
+        PfValidationResult result = resultIn;
+
+        if (collection == null) {
+            return result;
+        }
+
+        String prefix = fieldName + ".";
+        int count = 0;
+
+        for (T item : collection) {
+            result = validateNotNull(container, prefix + count, item, result);
+            ++count;
+        }
+
+        return result;
+    }
+
+    /**
+     * Invokes the "validate()" method on each item in a collection field, if the item is
+     * non-null.
+     *
+     * @param container the object that contains the field
+     * @param fieldName name of the field to be validated
+     * @param collection collection whose items are to be validated
+     * @param result where to place the result
+     * @return the result
+     */
+    public <T extends Validated> PfValidationResult validateCollection(@NonNull Object container,
+                    @NonNull String fieldName, Collection<T> collection, @NonNull PfValidationResult result) {
+
+        if (collection == null) {
+            return result;
+        }
+
+        for (T item : collection) {
+            if (item != null) {
+                result = item.validate(result);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Invokes the "validate()" method on each item in a concept collection field, if the
+     * item is non-null.
+     *
+     * @param container the object that contains the field
+     * @param fieldName name of the field to be validated
+     * @param collection collection whose items are to be validated
+     * @param result where to place the result
+     * @return the result
+     */
+    public <T extends PfConcept> PfValidationResult validateConceptCollection(@NonNull Object container,
+                    @NonNull String fieldName, Collection<T> collection, @NonNull PfValidationResult result) {
+
+        if (collection == null) {
+            return result;
+        }
+
+        for (T item : collection) {
+            if (item != null) {
+                result = item.validate(result);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Adds an error message to the validation result.
+     *
+     * @param container the object that contains the field
+     * @param fieldName name of the field to be validated
+     * @param result where to place the result
+     * @param errmsg the error message to be added, or {@code null} if nothing to add
+     */
+    public void addError(@NonNull Object container, @NonNull String fieldName, @NonNull PfValidationResult result,
+                    String errmsg) {
+        if (errmsg != null) {
+            result.addValidationMessage(new PfValidationMessage(makeKey(container), container.getClass(),
+                            ValidationResult.INVALID, fieldName + " invalid-" + errmsg));
+        }
+    }
+
+    /**
+     * Makes a PfKey suitable for insertion into a validation message. Note: the
+     * "toString()" method of the key simply invokes container.toString();
+     *
+     * @param container the container object for which the key should be made
+     * @return a key for the container
+     */
+    public PfKey makeKey(@NonNull Object container) {
+
+        return new PfConceptKey() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public String toString() {
+                return container.toString();
+            }
+        };
+    }
+}
diff --git a/models-base/src/test/java/org/onap/policy/models/base/ValidatedTest.java b/models-base/src/test/java/org/onap/policy/models/base/ValidatedTest.java
new file mode 100644
index 0000000..391e733
--- /dev/null
+++ b/models-base/src/test/java/org/onap/policy/models/base/ValidatedTest.java
@@ -0,0 +1,441 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP Policy Models
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.base;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ValidatedTest {
+    private static final String ERROR_MESSAGE = "error message";
+    private static final String COLLECTION_FIELD = "coll";
+    private static final String VALID_VALUE = "abc123";
+    private static final String PROPS_FIELD = "props";
+    private static final String MY_NAME = "my.name";
+    private static final String VALID_FIELD = "validField";
+    private static final String INVALID_FIELD = "invalidField";
+    private static final String NULL_FIELD = "nullField";
+    private static final String WORD_PAT = "\\w*";
+    private static final String MY_TO_STRING = "[some text]";
+    private static final String VERSION = "1.2.3";
+
+    private Validated validated;
+
+    @Before
+    public void setUp() {
+        validated = new Validated();
+    }
+
+    @Test
+    public void testValidate() {
+        assertThatThrownBy(() -> validated.validate(null)).isInstanceOf(NullPointerException.class);
+
+        PfValidationResult result = new PfValidationResult();
+        assertSame(result, validated.validate(result));
+        assertTrue(result.isValid());
+        assertEquals(0, result.getMessageList().size());
+    }
+
+    @Test
+    public void testValidateNotNull() {
+        PfValidationResult result = new PfValidationResult();
+
+        final PfValidationResult result2 = result;
+        assertThatThrownBy(() -> validated.validateNotNull(null, VALID_FIELD, VALID_VALUE, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateNotNull(this, null, VALID_VALUE, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateNotNull(this, VALID_FIELD, VALID_VALUE, null))
+                        .isInstanceOf(NullPointerException.class);
+
+        // null text
+        result = validated.validateNotNull(this, NULL_FIELD, null, result);
+
+        // invalid text
+        result = validated.validateNotNull(this, INVALID_FIELD, "!!!", result);
+
+        // valid text
+        result = validated.validateNotNull(this, VALID_FIELD, VALID_VALUE, result);
+
+        // different value
+        result = validated.validateNotNull(this, VALID_FIELD, Integer.valueOf(10), result);
+
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+
+        // check result for null text
+        PfValidationMessage msg = result.getMessageList().get(0);
+        assertEquals(ValidatedTest.class.getName(), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("nullField invalid-null"));
+    }
+
+    @Test
+    public void testValidateNotNullConceptKey() {
+        PfValidationResult result = new PfValidationResult();
+
+        // null key
+        PfConceptKey key = new PfConceptKey();
+        key.setVersion(VERSION);
+        result = validated.validateNotNull(key, result);
+
+        // null value
+        key = new PfConceptKey();
+        key.setName(MY_NAME);
+        result = validated.validateNotNull(key, result);
+
+        // both null
+        key = new PfConceptKey();
+        result = validated.validateNotNull(key, result);
+
+        assertFalse(result.isValid());
+        assertEquals(4, result.getMessageList().size());
+
+        // valid key & value
+        key = new PfConceptKey();
+        key.setName(MY_NAME);
+        key.setVersion(VERSION);
+        result = validated.validateNotNull(key, result);
+
+        // no change
+        assertFalse(result.isValid());
+        assertEquals(4, result.getMessageList().size());
+
+        Iterator<PfValidationMessage> it = result.getMessageList().iterator();
+
+        // check null key
+        PfValidationMessage msg = it.next();
+        assertEquals(PfConceptKey.class.getName(), msg.getObservedClass());
+        assertTrue(msg.getMessage().contains("name invalid-null"));
+
+        // check null value
+        msg = it.next();
+        assertEquals(PfConceptKey.class.getName(), msg.getObservedClass());
+        assertTrue(msg.getMessage().contains("version invalid-null"));
+
+        // check both null
+        msg = it.next();
+        assertEquals(PfConceptKey.class.getName(), msg.getObservedClass());
+        assertTrue(msg.getMessage().contains("name invalid-null"));
+        assertTrue(it.next().getMessage().contains("version invalid-null"));
+
+        final PfConceptKey key2 = key;
+        assertThatThrownBy(() -> validated.validateNotNull(key2, null)).isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateNotNull(null, new PfValidationResult()))
+                        .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void testValidateText() {
+        PfValidationResult result = new PfValidationResult();
+
+        final PfValidationResult result2 = result;
+        assertThatThrownBy(() -> validated.validateText(null, VALID_FIELD, VALID_VALUE, WORD_PAT, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateText(this, null, VALID_VALUE, WORD_PAT, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateText(this, VALID_FIELD, VALID_VALUE, null, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateText(this, VALID_FIELD, VALID_VALUE, WORD_PAT, null))
+                        .isInstanceOf(NullPointerException.class);
+
+        // null text
+        result = validated.validateText(this, NULL_FIELD, null, WORD_PAT, result);
+
+        // invalid text
+        result = validated.validateText(this, INVALID_FIELD, "!!!", WORD_PAT, result);
+
+        // valid text
+        result = validated.validateText(this, VALID_FIELD, VALID_VALUE, WORD_PAT, result);
+
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+
+        // check result for invalid text
+        PfValidationMessage msg = result.getMessageList().get(0);
+        assertEquals(ValidatedTest.class.getName(), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("invalidField invalid-parameter invalidField"));
+    }
+
+    @Test
+    public void testValidatePropertiesNotNull() {
+        PfValidationResult result = new PfValidationResult();
+        result = validated.validatePropertiesNotNull(this, "properties", null, result);
+        assertTrue(result.isValid());
+        assertEquals(0, result.getMessageList().size());
+
+        Map<String, Integer> map = new LinkedHashMap<>();
+
+        // null key
+        map.put(null, 10);
+
+        // null value
+        map.put("abc", null);
+
+        // valid key & value
+        map.put("def", 11);
+
+
+        result = validated.validatePropertiesNotNull(this, PROPS_FIELD, map, result);
+
+        assertFalse(result.isValid());
+        assertEquals(2, result.getMessageList().size());
+
+        Iterator<PfValidationMessage> it = result.getMessageList().iterator();
+
+        // check null key
+        PfValidationMessage msg = it.next();
+        assertEquals(ValidatedTest.class.getName(), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("props.null invalid-null"));
+
+        // check null value
+        msg = it.next();
+        assertEquals(ValidatedTest.class.getName(), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("props.abc invalid-null"));
+
+        final PfValidationResult result2 = result;
+        assertThatThrownBy(() -> validated.validatePropertiesNotNull(null, PROPS_FIELD, map, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validatePropertiesNotNull(this, null, map, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validatePropertiesNotNull(this, PROPS_FIELD, map, null))
+                        .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void testValidateCollectionNotNull() {
+        PfValidationResult result = new PfValidationResult();
+        result = validated.validateCollectionNotNull(this, "collection", null, result);
+        assertTrue(result.isValid());
+        assertEquals(0, result.getMessageList().size());
+
+        final List<String> lst = Arrays.asList("abc", null, "def", null);
+
+        result = validated.validateCollectionNotNull(this, COLLECTION_FIELD, lst, result);
+
+        assertFalse(result.isValid());
+        assertEquals(2, result.getMessageList().size());
+
+        Iterator<PfValidationMessage> it = result.getMessageList().iterator();
+
+        // check first item
+        PfValidationMessage msg = it.next();
+        assertEquals(ValidatedTest.class.getName(), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("coll.1 invalid-null"));
+
+        // check null value
+        msg = it.next();
+        assertEquals(ValidatedTest.class.getName(), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("coll.3 invalid-null"));
+
+        final PfValidationResult result2 = result;
+        assertThatThrownBy(() -> validated.validateCollectionNotNull(null, COLLECTION_FIELD, lst, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateCollectionNotNull(this, null, lst, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateCollectionNotNull(this, COLLECTION_FIELD, lst, null))
+                        .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void testValidateCollection() {
+        PfValidationResult result = new PfValidationResult();
+        result = validated.validateCollection(this, "collection", null, result);
+        assertTrue(result.isValid());
+        assertEquals(0, result.getMessageList().size());
+
+        List<MyValid> lst = Arrays.asList(new MyValid(0, false), new MyValid(1, true), null, new MyValid(2, false),
+                        new MyValid(3, true));
+        result = validated.validateCollection(this, COLLECTION_FIELD, lst, result);
+
+        assertFalse(result.isValid());
+        assertEquals(2, result.getMessageList().size());
+
+        Iterator<PfValidationMessage> it = result.getMessageList().iterator();
+
+        // check first item
+        PfValidationMessage msg = it.next();
+        assertEquals(MyValid.class.getName().replace('$', '.'), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("index.0 invalid-wrong value"));
+
+        // check null value
+        msg = it.next();
+        assertEquals(MyValid.class.getName().replace('$', '.'), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("index.2 invalid-wrong value"));
+
+        final PfValidationResult result2 = result;
+        assertThatThrownBy(() -> validated.validateCollection(null, COLLECTION_FIELD, lst, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateCollection(this, null, lst, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateCollection(this, COLLECTION_FIELD, lst, null))
+                        .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void testValidateConceptCollection() {
+        PfValidationResult result = new PfValidationResult();
+        result = validated.validateConceptCollection(this, "collection", null, result);
+        assertTrue(result.isValid());
+        assertEquals(0, result.getMessageList().size());
+
+        List<MyConcept> lst = Arrays.asList(new MyConcept(0, false), new MyConcept(1, true), null,
+                        new MyConcept(2, false), new MyConcept(3, true));
+        result = validated.validateConceptCollection(this, COLLECTION_FIELD, lst, result);
+
+        assertFalse(result.isValid());
+        assertEquals(2, result.getMessageList().size());
+
+        Iterator<PfValidationMessage> it = result.getMessageList().iterator();
+
+        // check first item
+        PfValidationMessage msg = it.next();
+        assertEquals(MyConcept.class.getName().replace('$', '.'), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("index.0 invalid-wrong value"));
+
+        // check null value
+        msg = it.next();
+        assertEquals(MyConcept.class.getName().replace('$', '.'), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("index.2 invalid-wrong value"));
+
+        final PfValidationResult result2 = result;
+        assertThatThrownBy(() -> validated.validateConceptCollection(null, COLLECTION_FIELD, lst, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateConceptCollection(this, null, lst, result2))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.validateConceptCollection(this, COLLECTION_FIELD, lst, null))
+                        .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void testAddError() {
+        final PfValidationResult result = new PfValidationResult();
+        final PfValidationResult result2 = result;
+
+        assertThatThrownBy(() -> validated.addError(null, VALID_FIELD, result2, ERROR_MESSAGE))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.addError(this, null, result2, ERROR_MESSAGE))
+                        .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> validated.addError(this, VALID_FIELD, null, ERROR_MESSAGE))
+                        .isInstanceOf(NullPointerException.class);
+
+        validated.addError(this, VALID_FIELD, result, "error-A");
+        validated.addError(this, VALID_FIELD, result, null);
+        validated.addError(this, VALID_FIELD, result, "error-B");
+
+        assertFalse(result.isValid());
+        assertEquals(2, result.getMessageList().size());
+
+        Iterator<PfValidationMessage> it = result.getMessageList().iterator();
+
+        PfValidationMessage msg = it.next();
+        assertEquals(ValidatedTest.class.getName(), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("validField invalid-error-A"));
+
+        msg = it.next();
+        assertEquals(ValidatedTest.class.getName(), msg.getObservedClass());
+        assertEquals(MY_TO_STRING, msg.getObservedKey().toString());
+        assertTrue(msg.getMessage().contains("validField invalid-error-B"));
+    }
+
+    @Test
+    public void testMakeKey() {
+        assertThatThrownBy(() -> validated.makeKey(null)).isInstanceOf(NullPointerException.class);
+
+        PfKey key = validated.makeKey(this);
+        assertEquals(MY_TO_STRING, key.toString());
+    }
+
+    @Override
+    public String toString() {
+        return MY_TO_STRING;
+    }
+
+    private static class MyValid extends Validated {
+        private boolean valid;
+        private int index;
+
+        public MyValid(int index, boolean valid) {
+            this.index = index;
+            this.valid = valid;
+        }
+
+        @Override
+        public PfValidationResult validate(PfValidationResult result) {
+            if (!valid) {
+                this.addError(this, "index." + index, result, "wrong value");
+            }
+
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return MY_TO_STRING;
+        }
+    }
+
+    private static class MyConcept extends PfConceptKey {
+        private static final long serialVersionUID = 1L;
+
+        private boolean valid;
+        private int index;
+
+        public MyConcept(int index, boolean valid) {
+            this.index = index;
+            this.valid = valid;
+        }
+
+        @Override
+        public PfValidationResult validate(PfValidationResult result) {
+            if (!valid) {
+                new Validated().addError(this, "index." + index, result, "wrong value");
+            }
+
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return MY_TO_STRING;
+        }
+    }
+}
diff --git a/models-base/src/test/java/org/onap/policy/models/base/keys/TestModels.java b/models-base/src/test/java/org/onap/policy/models/base/keys/TestModels.java
deleted file mode 100644
index a1bf3be..0000000
--- a/models-base/src/test/java/org/onap/policy/models/base/keys/TestModels.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- *  Copyright (C) 2019 Nordix Foundation.
- *  Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.models.base.keys;
-
-import com.openpojo.reflection.filters.FilterPackageInfo;
-import com.openpojo.validation.Validator;
-import com.openpojo.validation.ValidatorBuilder;
-import com.openpojo.validation.test.impl.GetterTester;
-import com.openpojo.validation.test.impl.SetterTester;
-
-import org.junit.Test;
-import org.onap.policy.common.utils.validation.ToStringTester;
-
-/**
- * Class to perform unit testing of models.
- *
- * @author Ram Krishna Verma (ram.krishna.verma@est.tech)
- */
-public class TestModels {
-
-    @Test
-    public void testModels() {
-        final Validator validator = ValidatorBuilder.create().with(new ToStringTester()).with(new SetterTester())
-                .with(new GetterTester()).build();
-        validator.validate(TestModels.class.getPackage().getName(), new FilterPackageInfo());
-    }
-}
diff --git a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpDeployPolicies.java
similarity index 64%
copy from models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
copy to models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpDeployPolicies.java
index 09e0381..1f4339f 100644
--- a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
+++ b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpDeployPolicies.java
@@ -18,25 +18,22 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.models.base.keys;
+package org.onap.policy.models.pap.concepts;
 
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-import org.onap.policy.models.base.PfConceptKey;
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
 
 /**
- * Identifies a policy type. Both the name and version must be non-null.
+ * Request deploy or update a set of policies using the <i>simple</i> PDP Group deployment
+ * REST API. Only the "name" and "policyVersion" fields of a Policy are used, and only the
+ * "name" field is actually required.
  */
-@NonNull
-@NoArgsConstructor
-public class PolicyTypeIdent extends PfConceptKey {
-    private static final long serialVersionUID = 1L;
-
-    public PolicyTypeIdent(String name, String version) {
-        super(name, version);
-    }
-
-    public PolicyTypeIdent(PolicyTypeIdent source) {
-        super(source);
-    }
+@Getter
+@Setter
+@ToString
+public class PdpDeployPolicies {
+    private List<PolicyIdentOptVersion> policies;
 }
diff --git a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpGroup.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpGroup.java
index d5e4191..29c4713 100644
--- a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpGroup.java
+++ b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpGroup.java
@@ -21,14 +21,11 @@
 
 package org.onap.policy.models.pap.concepts;
 
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import lombok.Getter;
-import lombok.NonNull;
 import lombok.Setter;
 import lombok.ToString;
-import org.onap.policy.models.base.PfUtils;
 import org.onap.policy.models.pdp.enums.PdpState;
 
 /**
@@ -47,25 +44,4 @@
     private String description;
     private Map<String, String> properties;
     private List<PdpSubGroup> pdpSubgroups;
-
-    /**
-     * Constructs the object.
-     */
-    public PdpGroup() {
-        super();
-    }
-
-    /**
-     * Constructs the object, making a deep copy from the source.
-     *
-     * @param source source from which to copy fields
-     */
-    public PdpGroup(@NonNull PdpGroup source) {
-        this.name = source.name;
-        this.version = source.version;
-        this.pdpGroupState = source.pdpGroupState;
-        this.description = source.description;
-        this.properties = (source.properties == null ? null : new LinkedHashMap<>(source.properties));
-        this.pdpSubgroups = PfUtils.mapList(source.pdpSubgroups, PdpSubGroup::new);
-    }
 }
diff --git a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpInstanceDetails.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpInstanceDetails.java
index c214c74..d584616 100644
--- a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpInstanceDetails.java
+++ b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpInstanceDetails.java
@@ -22,7 +22,6 @@
 package org.onap.policy.models.pap.concepts;
 
 import lombok.Getter;
-import lombok.NonNull;
 import lombok.Setter;
 import lombok.ToString;
 import org.onap.policy.models.pdp.enums.PdpHealthStatus;
@@ -42,23 +41,4 @@
     private PdpState pdpState;
     private PdpHealthStatus healthy;
     private String message;
-
-    /**
-     * Constructs the object.
-     */
-    public PdpInstanceDetails() {
-        super();
-    }
-
-    /**
-     * Constructs the object, making a deep copy from the source.
-     *
-     * @param source source from which to copy fields
-     */
-    public PdpInstanceDetails(@NonNull PdpInstanceDetails source) {
-        this.instanceId = source.instanceId;
-        this.pdpState = source.pdpState;
-        this.healthy = source.healthy;
-        this.message = source.message;
-    }
 }
diff --git a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpPolicies.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpPolicies.java
index e5ae24d..3111bec 100644
--- a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpPolicies.java
+++ b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpPolicies.java
@@ -26,6 +26,7 @@
 import lombok.Setter;
 import lombok.ToString;
 
+// TODO delete this once PAP has been modified to use PdpDeployPolicies
 /**
  * Request deploy or update a set of policies using the <i>simple</i> PDP Group deployment
  * REST API. Only the "name" and "policyVersion" fields of a Policy are used, and only the
diff --git a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpSubGroup.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpSubGroup.java
index 4335698..9989d25 100644
--- a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpSubGroup.java
+++ b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PdpSubGroup.java
@@ -21,15 +21,11 @@
 
 package org.onap.policy.models.pap.concepts;
 
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import lombok.Getter;
-import lombok.NonNull;
 import lombok.Setter;
 import lombok.ToString;
-import org.onap.policy.models.base.PfUtils;
-import org.onap.policy.models.base.keys.PolicyTypeIdent;
 
 /**
  * Class to represent a group of all PDP's of the same pdp type running for a particular
@@ -49,26 +45,4 @@
     private int desiredInstanceCount;
     private Map<String, String> properties;
     private List<PdpInstanceDetails> pdpInstances;
-
-    /**
-     * Constructs the object.
-     */
-    public PdpSubGroup() {
-        super();
-    }
-
-    /**
-     * Constructs the object, making a deep copy from the source.
-     *
-     * @param source source from which to copy fields
-     */
-    public PdpSubGroup(@NonNull PdpSubGroup source) {
-        this.pdpType = source.pdpType;
-        this.supportedPolicyTypes = PfUtils.mapList(source.supportedPolicyTypes, PolicyTypeIdent::new);
-        this.policies = PfUtils.mapList(source.policies, Policy::new);
-        this.currentInstanceCount = source.currentInstanceCount;
-        this.desiredInstanceCount = source.desiredInstanceCount;
-        this.properties = (source.properties == null ? null : new LinkedHashMap<>(source.properties));
-        this.pdpInstances = PfUtils.mapList(source.pdpInstances, PdpInstanceDetails::new);
-    }
 }
diff --git a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/Policy.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/Policy.java
index b83510e..541eb24 100644
--- a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/Policy.java
+++ b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/Policy.java
@@ -22,7 +22,6 @@
 package org.onap.policy.models.pap.concepts;
 
 import lombok.Getter;
-import lombok.NonNull;
 import lombok.Setter;
 import lombok.ToString;
 
@@ -41,24 +40,4 @@
     private String policyType;
     private String policyTypeVersion;
     private String policyTypeImpl;
-
-    /**
-     * Constructs the object.
-     */
-    public Policy() {
-        super();
-    }
-
-    /**
-     * Constructs the object, making a deep copy from the source.
-     *
-     * @param source source from which to copy fields
-     */
-    public Policy(@NonNull Policy source) {
-        this.name = source.name;
-        this.policyVersion = source.policyVersion;
-        this.policyType = source.policyType;
-        this.policyTypeVersion = source.policyTypeVersion;
-        this.policyTypeImpl = source.policyTypeImpl;
-    }
 }
diff --git a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyIdent.java
similarity index 65%
copy from models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
copy to models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyIdent.java
index 09e0381..2eb9df1 100644
--- a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
+++ b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyIdent.java
@@ -18,25 +18,19 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.models.base.keys;
+package org.onap.policy.models.pap.concepts;
 
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-import org.onap.policy.models.base.PfConceptKey;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
 
 /**
- * Identifies a policy type. Both the name and version must be non-null.
+ * Identifies a policy. Both the name and version must be non-null.
  */
-@NonNull
-@NoArgsConstructor
-public class PolicyTypeIdent extends PfConceptKey {
-    private static final long serialVersionUID = 1L;
-
-    public PolicyTypeIdent(String name, String version) {
-        super(name, version);
-    }
-
-    public PolicyTypeIdent(PolicyTypeIdent source) {
-        super(source);
-    }
+@Getter
+@Setter
+@ToString
+public class PolicyIdent {
+    private String name;
+    private String version;
 }
diff --git a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyIdentOptVersion.java
similarity index 66%
copy from models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
copy to models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyIdentOptVersion.java
index 09e0381..ecac2a2 100644
--- a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
+++ b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyIdentOptVersion.java
@@ -18,25 +18,22 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.models.base.keys;
+package org.onap.policy.models.pap.concepts;
 
-import lombok.NoArgsConstructor;
+import lombok.Getter;
 import lombok.NonNull;
-import org.onap.policy.models.base.PfConceptKey;
+import lombok.Setter;
+import lombok.ToString;
 
 /**
- * Identifies a policy type. Both the name and version must be non-null.
+ * Policy identifier with an optional version; only the "name" is required.
  */
-@NonNull
-@NoArgsConstructor
-public class PolicyTypeIdent extends PfConceptKey {
-    private static final long serialVersionUID = 1L;
+@Getter
+@Setter
+@ToString
+public class PolicyIdentOptVersion {
+    @NonNull
+    private String name;
 
-    public PolicyTypeIdent(String name, String version) {
-        super(name, version);
-    }
-
-    public PolicyTypeIdent(PolicyTypeIdent source) {
-        super(source);
-    }
+    private String version;
 }
diff --git a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyTypeIdent.java
similarity index 72%
copy from models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
copy to models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyTypeIdent.java
index 09e0381..3d466b7 100644
--- a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
+++ b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyTypeIdent.java
@@ -18,25 +18,21 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.models.base.keys;
+package org.onap.policy.models.pap.concepts;
 
-import lombok.NoArgsConstructor;
+import lombok.Getter;
 import lombok.NonNull;
-import org.onap.policy.models.base.PfConceptKey;
+import lombok.Setter;
+import lombok.ToString;
 
 /**
  * Identifies a policy type. Both the name and version must be non-null.
  */
+@Getter
+@Setter
+@ToString
 @NonNull
-@NoArgsConstructor
-public class PolicyTypeIdent extends PfConceptKey {
-    private static final long serialVersionUID = 1L;
-
-    public PolicyTypeIdent(String name, String version) {
-        super(name, version);
-    }
-
-    public PolicyTypeIdent(PolicyTypeIdent source) {
-        super(source);
-    }
+public class PolicyTypeIdent {
+    private String name;
+    private String version;
 }
diff --git a/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPdpGroup.java b/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPdpGroup.java
deleted file mode 100644
index ee52d82..0000000
--- a/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPdpGroup.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * ONAP Policy Models
- * ================================================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.models.pap.concepts;
-
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.Assert.assertEquals;
-
-import java.util.Arrays;
-import java.util.Map;
-import java.util.TreeMap;
-import org.junit.Test;
-import org.onap.policy.models.pdp.enums.PdpState;
-
-/**
- * Test the copy constructor, as {@link TestModels} tests the other methods.
- */
-public class TestPdpGroup {
-
-    @Test
-    public void testCopyConstructor() {
-        assertThatThrownBy(() -> new PdpGroup(null)).isInstanceOf(NullPointerException.class);
-
-        PdpGroup orig = new PdpGroup();
-
-        // verify with null values
-        assertEquals(orig.toString(), new PdpGroup(orig).toString());
-
-        // verify with all values
-        orig.setDescription("my-descript");
-        orig.setName("my-name");
-        orig.setPdpGroupState(PdpState.SAFE);
-
-        PdpSubGroup sub1 = new PdpSubGroup();
-        sub1.setCurrentInstanceCount(10);
-        PdpSubGroup sub2 = new PdpSubGroup();
-        sub2.setCurrentInstanceCount(11);
-        orig.setPdpSubgroups(Arrays.asList(sub1, sub2));
-
-        Map<String, String> props = new TreeMap<>();
-        props.put("key-A", "value-A");
-        props.put("key-B", "value-B");
-        orig.setProperties(props);
-
-        System.out.println("orig=" + orig);
-
-        assertEquals(orig.toString(), new PdpGroup(orig).toString());
-    }
-}
diff --git a/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPdpInstanceDetails.java b/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPdpInstanceDetails.java
deleted file mode 100644
index 65fbef6..0000000
--- a/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPdpInstanceDetails.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * ONAP Policy Models
- * ================================================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.models.pap.concepts;
-
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-import org.onap.policy.models.pdp.enums.PdpHealthStatus;
-import org.onap.policy.models.pdp.enums.PdpState;
-
-/**
- * Test the copy constructor, as {@link TestModels} tests the other methods.
- */
-public class TestPdpInstanceDetails {
-
-    @Test
-    public void testCopyConstructor() {
-        assertThatThrownBy(() -> new PdpInstanceDetails(null)).isInstanceOf(NullPointerException.class);
-
-        PdpInstanceDetails orig = new PdpInstanceDetails();
-
-        // verify with null values
-        assertEquals(orig.toString(), new PdpInstanceDetails(orig).toString());
-
-        // verify with all values
-        orig.setHealthy(PdpHealthStatus.TEST_IN_PROGRESS);
-        orig.setInstanceId("my-id");
-        orig.setMessage("my-message");
-        orig.setPdpState(PdpState.SAFE);
-        assertEquals(orig.toString(), new PdpInstanceDetails(orig).toString());
-    }
-}
diff --git a/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPdpSubGroup.java b/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPdpSubGroup.java
deleted file mode 100644
index 9af2f4e..0000000
--- a/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPdpSubGroup.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * ONAP Policy Models
- * ================================================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.models.pap.concepts;
-
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.Assert.assertEquals;
-
-import java.util.Arrays;
-import java.util.Map;
-import java.util.TreeMap;
-import org.junit.Test;
-import org.onap.policy.models.base.keys.PolicyTypeIdent;
-
-/**
- * Test the copy constructor, as {@link TestModels} tests the other methods.
- */
-public class TestPdpSubGroup {
-
-    @Test
-    public void testCopyConstructor() {
-        assertThatThrownBy(() -> new PdpSubGroup(null)).isInstanceOf(NullPointerException.class);
-
-        PdpSubGroup orig = new PdpSubGroup();
-
-        // verify with null values
-        assertEquals(orig.toString(), new PdpSubGroup(orig).toString());
-
-        // verify with all values
-        orig.setCurrentInstanceCount(10);
-        orig.setDesiredInstanceCount(11);
-
-        PdpInstanceDetails inst1 = new PdpInstanceDetails();
-        inst1.setInstanceId("my-id-A");
-        PdpInstanceDetails inst2 = new PdpInstanceDetails();
-        inst2.setInstanceId("my-id-B");
-        orig.setPdpInstances(Arrays.asList(inst1, inst2));
-
-        orig.setPdpType("my-type");
-
-        Policy pol1 = new Policy();
-        pol1.setName("policy-A");
-        Policy pol2 = new Policy();
-        pol2.setName("policy-B");
-        orig.setPolicies(Arrays.asList(pol1, pol2));
-
-        Map<String, String> props = new TreeMap<>();
-        props.put("key-A", "value-A");
-        props.put("key-B", "value-B");
-        orig.setProperties(props);
-
-        PolicyTypeIdent supp1 = new PolicyTypeIdent("supp-A", "1.2");
-        PolicyTypeIdent supp2 = new PolicyTypeIdent("supp-B", "3.4");
-        orig.setSupportedPolicyTypes(Arrays.asList(supp1, supp2));
-
-        assertEquals(orig.toString(), new PdpSubGroup(orig).toString());
-    }
-}
diff --git a/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPolicy.java b/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPolicy.java
deleted file mode 100644
index 6a042d3..0000000
--- a/models-pap/src/test/java/org/onap/policy/models/pap/concepts/TestPolicy.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * ONAP Policy Models
- * ================================================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.models.pap.concepts;
-
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-/**
- * Test the copy constructor, as {@link TestModels} tests the other methods.
- */
-public class TestPolicy {
-
-    @Test
-    public void testCopyConstructor() {
-        assertThatThrownBy(() -> new Policy(null)).isInstanceOf(NullPointerException.class);
-
-        Policy orig = new Policy();
-
-        // verify with null values
-        assertEquals(orig.toString(), new Policy(orig).toString());
-
-        // verify with all values
-        orig.setName("my-name");
-        orig.setPolicyType("my-type");
-        orig.setPolicyTypeImpl("my-impl");
-        orig.setPolicyTypeVersion("my-type-vers");
-        assertEquals(orig.toString(), new Policy(orig).toString());
-    }
-}
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpStatus.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpStatus.java
index f7b911f..814d357 100644
--- a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpStatus.java
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpStatus.java
@@ -25,7 +25,6 @@
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
-import org.onap.policy.models.base.keys.PolicyTypeIdent;
 import org.onap.policy.models.pdp.enums.PdpHealthStatus;
 import org.onap.policy.models.pdp.enums.PdpMessageType;
 import org.onap.policy.models.pdp.enums.PdpState;
diff --git a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PolicyIdent.java
similarity index 67%
copy from models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
copy to models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PolicyIdent.java
index 09e0381..6d6b6fe 100644
--- a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PolicyIdent.java
@@ -18,25 +18,33 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.models.base.keys;
+package org.onap.policy.models.pdp.concepts;
 
 import lombok.NoArgsConstructor;
 import lombok.NonNull;
 import org.onap.policy.models.base.PfConceptKey;
+import org.onap.policy.models.base.PfValidationResult;
+import org.onap.policy.models.base.Validated;
 
 /**
- * Identifies a policy type. Both the name and version must be non-null.
+ * Identifies a policy. Both the name and version must be non-null.
  */
 @NonNull
 @NoArgsConstructor
-public class PolicyTypeIdent extends PfConceptKey {
+public class PolicyIdent extends PfConceptKey {
     private static final long serialVersionUID = 1L;
+    private static final Validated validator = new Validated();
 
-    public PolicyTypeIdent(String name, String version) {
+    public PolicyIdent(String name, String version) {
         super(name, version);
     }
 
-    public PolicyTypeIdent(PolicyTypeIdent source) {
+    public PolicyIdent(PolicyIdent source) {
         super(source);
     }
+
+    @Override
+    public PfValidationResult validate(PfValidationResult result) {
+        return super.validate(validator.validateNotNull(this, result));
+    }
 }
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PolicyIdentOptVersion.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PolicyIdentOptVersion.java
new file mode 100644
index 0000000..a68a271
--- /dev/null
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PolicyIdentOptVersion.java
@@ -0,0 +1,61 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP Policy Models
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pdp.concepts;
+
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+import org.onap.policy.models.base.PfConceptKey;
+import org.onap.policy.models.base.PfKey;
+import org.onap.policy.models.base.PfValidationResult;
+import org.onap.policy.models.base.Validated;
+
+/**
+ * Policy identifier with an optional version; only the "name" is required.
+ */
+@NonNull
+@NoArgsConstructor
+public class PolicyIdentOptVersion extends PfConceptKey {
+    private static final long serialVersionUID = 1L;
+    private static final Validated validator = new Validated();
+
+
+    public PolicyIdentOptVersion(PolicyIdentOptVersion source) {
+        super(source);
+    }
+
+    /**
+     * Validates the object.
+     *
+     * @param resultIn where to place any errors
+     * @return a validation result
+     */
+    public PfValidationResult validate(@NonNull final PfValidationResult resultIn) {
+        PfValidationResult result = resultIn;
+
+        String name = getName();
+        if (PfConceptKey.NULL_KEY_NAME.equals(name)) {
+            validator.addError(this, "name", result, "null");
+        }
+        result = validator.validateText(this, "name", name, PfKey.NAME_REGEXP, result);
+
+        return validator.validateText(this, "version", getVersion(), PfKey.VERSION_REGEXP, result);
+    }
+}
diff --git a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PolicyTypeIdent.java
similarity index 79%
rename from models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
rename to models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PolicyTypeIdent.java
index 09e0381..ef67de8 100644
--- a/models-base/src/main/java/org/onap/policy/models/base/keys/PolicyTypeIdent.java
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PolicyTypeIdent.java
@@ -18,11 +18,13 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.models.base.keys;
+package org.onap.policy.models.pdp.concepts;
 
 import lombok.NoArgsConstructor;
 import lombok.NonNull;
 import org.onap.policy.models.base.PfConceptKey;
+import org.onap.policy.models.base.PfValidationResult;
+import org.onap.policy.models.base.Validated;
 
 /**
  * Identifies a policy type. Both the name and version must be non-null.
@@ -31,6 +33,7 @@
 @NoArgsConstructor
 public class PolicyTypeIdent extends PfConceptKey {
     private static final long serialVersionUID = 1L;
+    private static final Validated validator = new Validated();
 
     public PolicyTypeIdent(String name, String version) {
         super(name, version);
@@ -39,4 +42,9 @@
     public PolicyTypeIdent(PolicyTypeIdent source) {
         super(source);
     }
+
+    @Override
+    public PfValidationResult validate(PfValidationResult result) {
+        return super.validate(validator.validateNotNull(this, result));
+    }
 }
diff --git a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/IdentTestBase.java b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/IdentTestBase.java
new file mode 100644
index 0000000..10bc9a9
--- /dev/null
+++ b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/IdentTestBase.java
@@ -0,0 +1,81 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP Policy Models
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pdp.concepts;
+
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+
+/**
+ * Super class to test identity keys.
+ *
+ * @param <T> type of key being tested
+ */
+public class IdentTestBase<T> {
+
+    private static final Coder coder = new StandardCoder();
+
+    private final Class<T> clazz;
+
+
+    /**
+     * Constructs the object.
+     * @param clazz the type of class being tested
+     */
+    public IdentTestBase(Class<T> clazz) {
+        this.clazz = clazz;
+    }
+
+    /**
+     * Makes an identifier. Uses JSON which does no error checking.
+     *
+     * @param name name to put into the identifier
+     * @param version version to put into the identifier
+     * @return a new identifier
+     * @throws CoderException if the JSON cannot be decoded
+     */
+    public T makeIdent(String name, String version) throws CoderException {
+        StringBuilder bldr = new StringBuilder();
+        bldr.append("{");
+
+        if (name != null) {
+            bldr.append("'name':'");
+            bldr.append(name);
+            bldr.append("'");
+        }
+
+        if (version != null) {
+            if (name != null) {
+                bldr.append(',');
+            }
+
+            bldr.append("'version':'");
+            bldr.append(version);
+            bldr.append("'");
+        }
+
+        bldr.append("}");
+
+        String json = bldr.toString().replace('\'', '"');
+
+        return coder.decode(json, clazz);
+    }
+}
diff --git a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/TestPolicyIdent.java b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/TestPolicyIdent.java
new file mode 100644
index 0000000..4cd5570
--- /dev/null
+++ b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/TestPolicyIdent.java
@@ -0,0 +1,90 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP Policy Models
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pdp.concepts;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.onap.policy.models.base.PfValidationResult;
+
+/**
+ * Test the other constructors, as {@link TestModels} tests the other methods.
+ */
+public class TestPolicyIdent extends IdentTestBase<PolicyIdent> {
+    private static final String NAME = "my-name";
+    private static final String VERSION = "1.2.3";
+
+    public TestPolicyIdent() {
+        super(PolicyIdent.class);
+    }
+
+    @Test
+    public void testAllArgsConstructor() {
+        assertThatThrownBy(() -> new PolicyIdent(null, VERSION)).isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> new PolicyIdent(NAME, null)).isInstanceOf(NullPointerException.class);
+
+        PolicyIdent orig = new PolicyIdent(NAME, VERSION);
+        assertEquals(NAME, orig.getName());
+        assertEquals(VERSION, orig.getVersion());
+    }
+
+    @Test
+    public void testCopyConstructor() {
+        assertThatThrownBy(() -> new PolicyIdent(null)).isInstanceOf(NullPointerException.class);
+
+        PolicyIdent orig = new PolicyIdent();
+
+        // verify with null values
+        assertEquals(orig.toString(), new PolicyIdent(orig).toString());
+
+        // verify with all values
+        orig = new PolicyIdent(NAME, VERSION);
+        assertEquals(orig.toString(), new PolicyIdent(orig).toString());
+    }
+
+    @Test
+    public void testValidate() throws Exception {
+        assertTrue(makeIdent(NAME, VERSION).validate(new PfValidationResult()).isValid());
+
+        // everything is null
+        PfValidationResult result = makeIdent(null, null).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(2, result.getMessageList().size());
+
+        // name is null
+        result = makeIdent(null, VERSION).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+
+        // version is null
+        result = makeIdent(NAME, null).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+
+        // version is invalid
+        result = makeIdent(NAME, "!!!" + VERSION).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+    }
+}
diff --git a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/TestPolicyIdentOptVersion.java b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/TestPolicyIdentOptVersion.java
new file mode 100644
index 0000000..3428ac1
--- /dev/null
+++ b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/TestPolicyIdentOptVersion.java
@@ -0,0 +1,87 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP Policy Models
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pdp.concepts;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.onap.policy.models.base.PfValidationResult;
+
+/**
+ * Test the other constructors, as {@link TestModels} tests the other methods.
+ */
+public class TestPolicyIdentOptVersion extends IdentTestBase<PolicyIdentOptVersion> {
+    private static final String NAME = "my-name";
+    private static final String VERSION = "1.2.3";
+
+    public TestPolicyIdentOptVersion() {
+        super(PolicyIdentOptVersion.class);
+    }
+
+    @Test
+    public void testCopyConstructor() throws Exception {
+        assertThatThrownBy(() -> new PolicyIdentOptVersion(null)).isInstanceOf(NullPointerException.class);
+
+        PolicyIdentOptVersion orig = new PolicyIdentOptVersion();
+
+        // verify with null values
+        assertEquals(orig.toString(), new PolicyIdentOptVersion(orig).toString());
+
+        // verify with all values
+        orig = makeIdent(NAME, VERSION);
+        assertEquals(orig.toString(), new PolicyIdentOptVersion(orig).toString());
+    }
+
+    @Test
+    public void testValidate() throws Exception {
+        assertThatThrownBy(() -> makeIdent(NAME, VERSION).validate(null)).isInstanceOf(NullPointerException.class);
+        assertTrue(makeIdent(NAME, VERSION).validate(new PfValidationResult()).isValid());
+        assertTrue(makeIdent(NAME, null).validate(new PfValidationResult()).isValid());
+
+        // everything is null
+        PfValidationResult result = makeIdent(null, null).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+
+        // name is null
+        result = makeIdent(null, VERSION).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+
+        // name is null, version is invalid
+        result = makeIdent(null, "$$$" + VERSION).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(2, result.getMessageList().size());
+
+        // name is invalid
+        result = makeIdent("!!!invalid name$$$", VERSION).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+
+        // version is invalid
+        result = makeIdent(NAME, "!!!" + VERSION).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+    }
+}
diff --git a/models-base/src/test/java/org/onap/policy/models/base/keys/TestPolicyTypeIdent.java b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/TestPolicyTypeIdent.java
similarity index 62%
rename from models-base/src/test/java/org/onap/policy/models/base/keys/TestPolicyTypeIdent.java
rename to models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/TestPolicyTypeIdent.java
index 1638a87..5b7494e 100644
--- a/models-base/src/test/java/org/onap/policy/models/base/keys/TestPolicyTypeIdent.java
+++ b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/TestPolicyTypeIdent.java
@@ -18,22 +18,27 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.models.base.keys;
+package org.onap.policy.models.pdp.concepts;
 
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
-import org.onap.policy.models.base.keys.PolicyTypeIdent;
-import org.onap.policy.models.base.keys.TestModels;
+import org.onap.policy.models.base.PfValidationResult;
 
 /**
  * Test the other constructors, as {@link TestModels} tests the other methods.
  */
-public class TestPolicyTypeIdent {
+public class TestPolicyTypeIdent extends IdentTestBase<PolicyTypeIdent> {
     private static final String NAME = "my-name";
     private static final String VERSION = "1.2.3";
 
+    public TestPolicyTypeIdent() {
+        super(PolicyTypeIdent.class);
+    }
+
     @Test
     public void testAllArgsConstructor() {
         assertThatThrownBy(() -> new PolicyTypeIdent(null, VERSION)).isInstanceOf(NullPointerException.class);
@@ -57,4 +62,30 @@
         orig = new PolicyTypeIdent(NAME, VERSION);
         assertEquals(orig.toString(), new PolicyTypeIdent(orig).toString());
     }
+
+    @Test
+    public void testValidate() throws Exception {
+        assertTrue(makeIdent(NAME, VERSION).validate(new PfValidationResult()).isValid());
+
+        // everything is null
+        PfValidationResult result = makeIdent(null, null).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(2, result.getMessageList().size());
+
+        // name is null
+        result = makeIdent(null, VERSION).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+
+        // version is null
+        result = makeIdent(NAME, null).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+
+        // version is invalid
+        result = makeIdent(NAME, "!!!" + VERSION).validate(new PfValidationResult());
+        assertFalse(result.isValid());
+        assertEquals(1, result.getMessageList().size());
+    }
+
 }