blob: db96e3b8a1204dd982ddf9b27c000e333b11ed7a [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
#include "DFPlatform.h"
#include "DFBDT.h"
#include "DFDOM.h"
#include "DFXML.h"
#include "DFCommon.h"
#include "DFHashTable.h"
#include <assert.h>
#include <stdlib.h>
int nullIsVisible(void *ctx, DFNode *concrete)
{
return 0;
}
DFNode *BDTContainerGet(void *ctx, DFLens *theLens, DFNode *abstract, DFNode *concrete)
{
assert(abstract != NULL);
if ((theLens->isVisible != NULL) && (theLens->get != NULL)) {
for (DFNode *child = concrete->first; child != NULL; child = child->next) {
DFNode *absChild = theLens->get(ctx,child);
if (absChild != NULL)
DFAppendChild(abstract,absChild);
}
}
return abstract;
}
void BDTContainerPut(void *ctx, DFLens *theLens, DFNode *abstract, DFNode *concrete,
DFLookupConcreteFunction lookupConcrete)
{
int (*isVisible)(void *ctx, DFNode *concrete) = (theLens->isVisible != NULL) ? theLens->isVisible : nullIsVisible;
int count = 0;
for (DFNode *abs = abstract->first; abs != NULL; abs = abs->next)
count++;
DFNode **conChildren = (DFNode **)xmalloc(count*sizeof(DFNode*));
count = 0;
for (DFNode *abs = abstract->first; abs != NULL; abs = abs->next) {
DFNode *con = NULL;
if (lookupConcrete != NULL)
con = lookupConcrete(ctx,abs);
if (con == NULL) {
if (theLens->create != NULL)
con = theLens->create(ctx,abs);
}
else {
if (theLens->put != NULL)
theLens->put(ctx,abs,con);
}
if (con != NULL)
conChildren[count++] = con;
}
DFHashTable *oldPrevHidden = DFHashTableNew(NULL,NULL);
for (int i = count-1; i >= 0; i--) {
DFNode *con = conChildren[i];
DFNode *prevHidden = con->prev;
while ((prevHidden != NULL) && isVisible(ctx,prevHidden))
prevHidden = prevHidden->prev;
if (prevHidden != NULL) {
DFHashTableAddInt(oldPrevHidden,con->seqNo,prevHidden);
}
else {
DFHashTableAddInt(oldPrevHidden,con->seqNo,(void *)1);
}
}
// Remove concrete nodes for which their abstract counterparts no longer exist
DFHashTable *remaining = DFHashTableNew(NULL,NULL);
for (int i = 0; i < count; i++)
DFHashTableAddInt(remaining,conChildren[i]->seqNo,"");
DFNode *next;
for (DFNode *con = concrete->first; con != NULL; con = next) {
next = con->next;
if (isVisible(ctx,con)) {
if (DFHashTableLookupInt(remaining,con->seqNo) == NULL) {
if (theLens->remove != NULL)
theLens->remove(ctx,con);
DFRemoveNode(con);
}
}
}
// Find the last node
DFNode *last = NULL;
if (concrete->last != NULL) {
last = concrete->last;
while ((last->prev != NULL) && !isVisible(ctx,last->prev))
last = last->prev;
}
// Reinsert all the nodes in the correct order
for (int i = count-1; i >= 0; i--) {
DFNode *con = conChildren[i];
DFNode *oldNext = con->next;
while ((oldNext != NULL) && !isVisible(ctx,oldNext))
oldNext = oldNext->next;
DFNode *newNext = (i+1 < count) ? conChildren[i+1] : NULL;
if (newNext == NULL)
newNext = last;
DFInsertBefore(concrete,con,newNext);
}
// Fixup stage - move nodes backwards as much as possible to their previous prevHidden
for (DFNode *con = concrete->first; con != NULL; con = next) {
next = con->next;
if (!isVisible(ctx,con))
continue;
void *ph = DFHashTableLookupInt(oldPrevHidden,con->seqNo);
if (ph == NULL)
continue;
if (ph == (void*)1)
ph = NULL;;
DFNode *prevHidden = (DFNode *)ph;
if (prevHidden == NULL)
continue;
DFNode *insertionPoint = con->next;
DFNode *actual = con->prev;
int blockedByPrev = 0;
int found = 0;
for (;;) {
if (!blockedByPrev) {
if (actual == NULL)
insertionPoint = concrete->first;
else
insertionPoint = actual->next;
}
if ((actual != NULL) && isVisible(ctx,actual))
blockedByPrev = 1;
if (actual == prevHidden) {
found = 1;
break;
}
if (actual == NULL) {
break;
}
actual = actual->prev;
}
if (found)
DFInsertBefore(concrete,con,insertionPoint);
}
free(conChildren);
DFHashTableRelease(oldPrevHidden);
DFHashTableRelease(remaining);
}