/*
* ModeShape (http://www.modeshape.org)
*
* 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.
*/
package org.modeshape.sequencer.teiid.model;
import static org.modeshape.sequencer.teiid.lexicon.TransformLexicon.Namespace.URI;
import javax.jcr.Node;
import javax.jcr.Value;
import org.modeshape.common.util.CheckArg;
import org.modeshape.common.util.StringUtil;
import org.modeshape.jcr.api.JcrConstants;
import org.modeshape.sequencer.teiid.lexicon.TransformLexicon;
import org.modeshape.sequencer.teiid.lexicon.TransformLexicon.JcrId;
import org.modeshape.sequencer.teiid.lexicon.TransformLexicon.ModelId;
import org.modeshape.sequencer.teiid.lexicon.XmiLexicon;
import org.modeshape.sequencer.teiid.model.ReferenceResolver.UnresolvedReference;
import org.modeshape.sequencer.teiid.xmi.XmiElement;
/**
* The model object handler for the {@link org.modeshape.sequencer.teiid.lexicon.TransformLexicon.Namespace#URI transformation}
* namespace.
*/
public final class TransformationModelObjectHandler extends ModelObjectHandler {
/**
* @see org.modeshape.sequencer.teiid.model.ModelObjectHandler#process(org.modeshape.sequencer.teiid.xmi.XmiElement,
* javax.jcr.Node)
*/
@Override
protected void process( final XmiElement element,
final Node parentNode ) throws Exception {
CheckArg.isNotNull(element, "element");
CheckArg.isNotNull(parentNode, "node");
CheckArg.isEquals(element.getNamespaceUri(), "namespace URI", URI, "relational URI");
LOGGER.debug("==== TransformationModelObjectHandler:process:element={0}", element.getName());
if (ModelId.TRANSFORMATION_CONTAINER.equals(element.getName())) {
// just process children
for (final XmiElement kid : element.getChildren()) {
if (ModelId.TRANSFORMATION_MAPPINGS.equals(kid.getName())) {
processMappings(kid);
} else {
LOGGER.debug("**** transformation container child of '{0}' was not processed", kid.getName());
}
}
} else {
LOGGER.debug("**** transformation type of '{0}' was not processed", element.getName());
}
}
private void processInputs( final XmiElement inputs,
final Node transformed,
final UnresolvedReference unresolved ) throws Exception {
assert (inputs != null);
assert (ModelId.INPUTS.equals(inputs.getName()));
// inputs is a referenced source table or a referenced column
final ReferenceResolver resolver = getResolver();
final String inputHref = inputs.getAttributeValue(ModelId.HREF, URI);
final String inputUuid = resolver.resolveInternalReference(inputHref);
// - transform:transformedFrom (weakreference)
setReference(transformed, unresolved, JcrId.TRANSFORMED_FROM, inputUuid, true);
// - transform:transformedFromHrefs (string)
if (transformed != null) {
addPropertyValue(transformed, JcrId.TRANSFORMED_FROM_HREFS, inputHref);
} else if (unresolved != null) {
unresolved.addProperty(JcrId.TRANSFORMED_FROM_HREFS, inputHref, true);
} else {
assert false;
}
// - transform:transformedFromXmiUuids (string)
if (transformed != null) {
addPropertyValue(transformed, JcrId.TRANSFORMED_FROM_XMI_UUIDS, inputUuid);
} else if (unresolved != null) {
unresolved.addProperty(JcrId.TRANSFORMED_FROM_XMI_UUIDS, inputUuid, true);
} else {
assert false;
}
// get the referenced source table or column node in order to get its name
final Node source = resolver.getNode(inputUuid);
// - transform:transformedFromNames (string)
if ((transformed != null) && (source != null)) {
addPropertyValue(transformed, JcrId.TRANSFORMED_FROM_NAMES, source.getName());
} else {
String referencerUuid = null;
if (unresolved != null) {
referencerUuid = unresolved.getUuid();
} else if (transformed != null) {
referencerUuid = transformed.getProperty(XmiLexicon.JcrId.UUID).getString();
} else {
assert false;
}
if (source == null) {
UnresolvedReference unresolvedSource = resolver.addUnresolvedReference(inputUuid);
unresolvedSource.addResolvedName(referencerUuid, JcrId.TRANSFORMED_FROM_NAMES);
} else {
if (unresolved != null) {
unresolved.addProperty(JcrId.TRANSFORMED_FROM_NAMES, source.getName(), true);
} else {
assert false;
}
}
}
}
private void processMappings( final XmiElement mappings ) throws Exception {
assert (mappings != null);
assert (ModelId.TRANSFORMATION_MAPPINGS.equals(mappings.getName()));
LOGGER.debug("=========mappings='{0}'", mappings.getName());
final ReferenceResolver resolver = getResolver();
final String targetUuid = mappings.getAttributeValue(ModelId.TARGET, URI);
final String uuid = resolver.resolveInternalReference(targetUuid);
final Node targetNode = resolver.getNode(uuid);
UnresolvedReference unresolved = null;
// add SQL mixin
if (targetNode == null) {
unresolved = resolver.addUnresolvedReference(uuid);
unresolved.addMixin(JcrId.WITH_SQL);
unresolved.addMixin(JcrId.TRANSFORMED);
} else if (!targetNode.isNodeType(JcrId.WITH_SQL)) {
targetNode.addMixin(JcrId.WITH_SQL);
targetNode.addMixin(JcrId.TRANSFORMED);
}
// Get the transformation details ...
for (final XmiElement kid : mappings.getChildren()) {
if (ModelId.HELPER.equals(kid.getName())) {
final XmiElement helperNested = kid.findChild(ModelId.NESTED, URI);
if (helperNested == null) {
continue;
}
setNestedProperty(targetNode, unresolved, helperNested, JcrId.SELECT_SQL, ModelId.SELECT_SQL);
setNestedProperty(targetNode, unresolved, helperNested, JcrId.INSERT_SQL, ModelId.INSERT_SQL);
setNestedProperty(targetNode, unresolved, helperNested, JcrId.UPDATE_SQL, ModelId.UPDATE_SQL);
setNestedProperty(targetNode, unresolved, helperNested, JcrId.DELETE_SQL, ModelId.DELETE_SQL);
setNestedProperty(targetNode, unresolved, helperNested, JcrId.INSERT_ALLOWED, ModelId.INSERT_ALLOWED);
setNestedProperty(targetNode, unresolved, helperNested, JcrId.UPDATE_ALLOWED, ModelId.UPDATE_ALLOWED);
setNestedProperty(targetNode, unresolved, helperNested, JcrId.DELETE_ALLOWED, ModelId.DELETE_ALLOWED);
setNestedProperty(targetNode, unresolved, helperNested, JcrId.INSERT_SQL_DEFAULT, ModelId.INSERT_SQL_DEFAULT);
setNestedProperty(targetNode, unresolved, helperNested, JcrId.UPDATE_SQL_DEFAULT, ModelId.UPDATE_SQL_DEFAULT);
setNestedProperty(targetNode, unresolved, helperNested, JcrId.DELETE_SQL_DEFAULT, ModelId.DELETE_SQL_DEFAULT);
} else if (ModelId.INPUTS.equals(kid.getName())) {
// input is the source tables
processInputs(kid, targetNode, unresolved);
} else if (ModelId.NESTED.equals(kid.getName())) {
processNested(kid);
} else {
LOGGER.debug("**** transformation mapping child type of '{0}' was not processed", kid);
}
}
}
private void processNested( final XmiElement nested ) throws Exception {
assert (nested != null);
assert (ModelId.NESTED.equals(nested.getName()));
// nested is a virtual column to source column(s) mapping
// nested/outputs attribute is the virtual column
// nested/inputs child element href attribute is the source column
final ReferenceResolver resolver = getResolver();
final String columnHref = nested.getAttributeValue(ModelId.OUTPUTS, URI);
final String columnUuid = resolver.resolveInternalReference(columnHref);
// virtual column node
final Node columnNode = resolver.getNode(columnUuid);
UnresolvedReference unresolvedColumn = null;
if (columnNode == null) {
unresolvedColumn = resolver.addUnresolvedReference(columnUuid);
unresolvedColumn.addMixin(JcrId.TRANSFORMED);
} else if (!columnNode.isNodeType(JcrId.TRANSFORMED)) {
columnNode.addMixin(JcrId.TRANSFORMED);
}
// nested children are inputs which are the referenced source columns
for (final XmiElement inputs : nested.getChildren()) {
if (ModelId.INPUTS.equals(inputs.getName())) {
processInputs(inputs, columnNode, unresolvedColumn);
}
}
}
private void setNestedProperty( final Node targetNode,
final UnresolvedReference unresolved,
final XmiElement nested,
final String propertyName,
final String attributeName ) throws Exception {
assert (nested != null);
assert ((propertyName != null) && !propertyName.isEmpty());
assert ((attributeName != null) && !attributeName.isEmpty());
final String value = nested.getAttributeValue(attributeName, TransformLexicon.Namespace.URI);
if (!StringUtil.isBlank(value)) {
if (targetNode == null) {
unresolved.addProperty(propertyName, value, false);
} else {
setProperty(targetNode, propertyName, value);
}
}
}
private void setReference( final Node referencerNode,
final UnresolvedReference unresolvedReferencer,
final String propertyName,
final String referencedUuid,
final boolean multiValuedProperty ) throws Exception {
assert ((referencerNode != null) || (unresolvedReferencer != null));
assert ((propertyName != null) && !propertyName.isEmpty());
assert ((referencedUuid != null) && !referencedUuid.isEmpty());
// sets a weak reference
final ReferenceResolver resolver = getResolver();
final Node referencedNode = resolver.getNode(referencedUuid);
UnresolvedReference unresolvedReference = null;
if (unresolvedReferencer != null) {
// add reference
unresolvedReferencer.addReference(propertyName, referencedUuid);
} else {
assert referencerNode != null;
if (referencedNode == null) {
// resolved referencer, unresolved referenced
unresolvedReference = resolver.addUnresolvedReference(referencedUuid);
unresolvedReference.addReferencerReference(referencerNode.getProperty(XmiLexicon.JcrId.UUID).getString(),
propertyName);
} else {
// resolved referencer, resolved referenced
if (!referencedNode.isNodeType(JcrConstants.MIX_REFERENCEABLE)) {
referencedNode.addMixin(JcrConstants.MIX_REFERENCEABLE);
}
final Value weakRef = referencerNode.getSession().getValueFactory().createValue(referencedNode, true);
if (multiValuedProperty) {
Value[] currentValues = null;
Value[] newValues = null;
if (referencedNode.hasProperty(propertyName)) {
currentValues = referencedNode.getProperty(propertyName).getValues();
newValues = new Value[currentValues.length + 1];
System.arraycopy(currentValues, 0, newValues, 0, currentValues.length);
newValues[currentValues.length] = weakRef;
} else {
newValues = new Value[] {weakRef};
}
referencedNode.setProperty(propertyName, newValues);
} else {
// single valued
referencerNode.setProperty(propertyName, weakRef);
}
}
}
}
}