com.redhat.lightblue.metadata.PredefinedFields Maven / Gradle / Ivy
/*
Copyright 2013 Red Hat, Inc. and/or its affiliates.
This file is part of lightblue.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package com.redhat.lightblue.metadata;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.NullNode;
import com.redhat.lightblue.metadata.constraints.RequiredConstraint;
import com.redhat.lightblue.metadata.constraints.StringLengthConstraint;
import com.redhat.lightblue.metadata.types.IntegerType;
import com.redhat.lightblue.metadata.types.StringType;
import com.redhat.lightblue.util.Error;
import com.redhat.lightblue.util.JsonDoc;
import com.redhat.lightblue.util.Path;
/**
* Ensures that the predefined fields are included in the metadata.
*
*
* - _id, type is string, int or biginteger. Unique constraint, roles setup
* to allow read by anyone, noone updates
* - objectType, type is string. required and minimum length=1, roles setup
* to allow read by anyone, noone updates
* - for every array field with name "x", a field "x#" of type int, roles
* setup to allow read by anyone, noone updates
*
*/
public final class PredefinedFields {
public static final String OBJECTTYPE_FIELD = "objectType";
public static final String FIELD_ARRAY_COUNT_POSTFIX = "#";
public static final Path OBJECTTYPE_PATH = new Path(OBJECTTYPE_FIELD);
public static void ensurePredefinedFields(EntityMetadata md) {
ensureObjectType(md);
// Recursively find all arrays, and add array size fields
List l = new ArrayList<>();
// We have to go through all the array fields, and queue up
// the new size fields to add, otherwise the following loop
// throws a concurrent modification exception
FieldCursor cursor = md.getFieldCursor();
while (cursor.next()) {
FieldTreeNode f = cursor.getCurrentNode();
if (f instanceof ArrayField) {
ParentNewChild x = ensureArraySize(md, (ArrayField) f);
if (x != null) {
l.add(x);
}
}
}
for (ParentNewChild x : l) {
x.parent.addNew(x.newChild);
}
}
/**
* Updates all array size values in the given document
*/
public static void updateArraySizes(EntityMetadata md, JsonNodeFactory factory, JsonDoc doc) {
FieldCursor cursor = md.getFieldCursor();
while (cursor.next()) {
FieldTreeNode f = cursor.getCurrentNode();
if (doesFieldNameMatchArrayCountPattern(f.getName())) {
Path lengthField = cursor.getCurrentPath();
String ls = lengthField.toString();
Path arrField = new Path(ls.substring(0, ls.length() - 1));
try {
if (md.resolve(arrField) != null) {
JsonNode arrNode = doc.get(arrField);
if (arrNode == null || arrNode instanceof NullNode) {
doc.modify(lengthField, factory.numberNode(0), false);
} else {
doc.modify(lengthField, factory.numberNode(arrNode.size()), false);
}
}
} catch (Exception e) {
}
}
}
}
private static void ensureObjectType(EntityMetadata md) {
Field f = md.getFields().getField(OBJECTTYPE_FIELD);
if (f == null) {
f = new SimpleField(OBJECTTYPE_FIELD, StringType.TYPE);
md.getFields().addNew(f);
}
// Object type must be string
if (f instanceof SimpleField
&& f.getType().equals(StringType.TYPE)) {
// Required constraint
if (findConstraint(f.getConstraints(), new ConstraintSearchCB() {
@Override
public boolean checkMatch(FieldConstraint c) {
return c instanceof RequiredConstraint;
}
}) == null) {
f.setConstraints(addConstraint(f.getConstraints(), new RequiredConstraint()));
}
// Can't be empty
if (findConstraint(f.getConstraints(), new ConstraintSearchCB() {
@Override
public boolean checkMatch(FieldConstraint c) {
if (c instanceof StringLengthConstraint && ((StringLengthConstraint) c).getType().equals(StringLengthConstraint.MINLENGTH)) {
return true;
}
return false;
}
}) == null) {
f.setConstraints(addConstraint(f.getConstraints(), new StringLengthConstraint(StringLengthConstraint.MINLENGTH, 1)));
}
setRoleIfEmpty(f.getAccess().getFind(), MetadataConstants.ROLE_ANYONE);
setRoleIfEmpty(f.getAccess().getUpdate(), MetadataConstants.ROLE_NOONE);
} else {
throw Error.get(MetadataConstants.ERR_FIELD_WRONG_TYPE, OBJECTTYPE_FIELD + ":" + f.getType().getName());
}
}
private static void setRoleIfEmpty(Access access, String role) {
if (access.getRoles().isEmpty()) {
List l = new ArrayList<>(1);
l.add(role);
access.setRoles(l);
}
}
private interface ConstraintSearchCB {
boolean checkMatch(T c);
}
private static T findConstraint(List list, ConstraintSearchCB cb) {
if (list != null) {
for (T x : list) {
if (cb.checkMatch(x)) {
return x;
}
}
}
return null;
}
private static List addConstraint(List constraints, T newConstraint) {
List ret = constraints == null ? new ArrayList(1) : constraints;
ret.add(newConstraint);
return ret;
}
private static final class ParentNewChild {
private final Fields parent;
private final Field newChild;
public ParentNewChild(Fields parent, Field newChild) {
this.parent = parent;
this.newChild = newChild;
}
}
private static ParentNewChild ensureArraySize(EntityMetadata md, ArrayField arr) {
// Get the parent. The parent is either an object field, object element, or the root
FieldTreeNode parent = arr.getParent();
Fields fields;
if (parent instanceof ObjectField) {
fields = ((ObjectField) parent).getFields();
} else if (parent instanceof ObjectArrayElement) {
fields = ((ObjectArrayElement) parent).getFields();
} else {
fields = md.getFields();
}
String fieldName = createArrayCountFieldName(arr.getName());
Field f = fields.getField(fieldName);
ParentNewChild ret;
if (f == null) {
f = new SimpleField(fieldName, IntegerType.TYPE);
/*
* If array has Find roles on it, then also set on count field.
* Other roles should not be copied over as they may cause problems
* with save operations.
*/
f.getAccess().getFind().setRoles(arr.getAccess().getFind());
ret = new ParentNewChild(fields, f);
} else {
ret = null;
}
// Must be int
if (f instanceof SimpleField
&& f.getType().equals(IntegerType.TYPE)) {
setRoleIfEmpty(f.getAccess().getFind(), MetadataConstants.ROLE_ANYONE);
} else {
throw Error.get(MetadataConstants.ERR_FIELD_WRONG_TYPE, fieldName + ":" + f.getType().getName());
}
return ret;
}
/**
* Returns true
if the passed in field name is the object type, otherwise false
.
* @param fieldName - field name to test.
* @return true
if the passed in field name is the object type, otherwise false
.
*/
public static boolean isFieldObjectType(String fieldName){
return OBJECTTYPE_FIELD.equalsIgnoreCase(fieldName);
}
/**
* Returns true
if the passed in field name is the count field for an array field,
* otherwise false
.
* @param fieldName - field name to test.
* @param metadataFields - {@link Fields} from entity metadata.
* @return true
if the passed in field name is the count field for an array field,
* otherwise false
.
*/
public static boolean isFieldAnArrayCount(String fieldName, Fields metadataFields){
if(!doesFieldNameMatchArrayCountPattern(fieldName)){
return false;
}
/*
* Ensure that the actual array field exists also, otherwise it might be a field
* that simply ends with a '#' character.
*/
Field field = metadataFields.getField(createArrayFieldNameFromCountField(fieldName));
if((field != null) && (field instanceof ArrayField)){
return true;
}
return false;
}
public static boolean isPredefinedField(String fieldName, Fields fields) {
return PredefinedFields.isFieldObjectType(fieldName)
|| PredefinedFields.isFieldAnArrayCount(fieldName, fields);
}
/**
* Creates and returns the array field name for the passed in array count field name.
* @param countField - array count field.
* @return the array field name for the passed in array count field name.
*/
public static String createArrayFieldNameFromCountField(String countField){
if(doesFieldNameMatchArrayCountPattern(countField)){
return countField.substring(0, countField.length() - 1);
}
return countField;
}
/**
* Created and returns the name of the array count field for the passed in array field name.
* @param arrayFieldName - array field name.
* @return name of the array count field for the passed in array field name.
*/
public static String createArrayCountFieldName(String arrayFieldName){
return arrayFieldName + FIELD_ARRAY_COUNT_POSTFIX;
}
/**
* Returns true
if the passed in field name matches the array count field name pattern,
* otherwise false
.
* @param fieldName - field name to test.
* @return true
if the passed in field name matches the array count field name pattern,
* otherwise false
.
*/
static boolean doesFieldNameMatchArrayCountPattern(String fieldName){
if(fieldName == null){
return false;
}
return fieldName.endsWith(FIELD_ARRAY_COUNT_POSTFIX);
}
private PredefinedFields() {
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy