/*
* Copyright 2014 Igor Maznitsa (http://www.igormaznitsa.com).
*
* 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 com.igormaznitsa.prol.logic;
import com.igormaznitsa.prol.data.Term;
import com.igormaznitsa.prol.data.TermList;
import com.igormaznitsa.prol.data.TermStruct;
import com.igormaznitsa.prol.data.Var;
import com.igormaznitsa.prol.utils.IntegerHashSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* This class describes a variable state container for a goal. It allows to save
* and restore variable values
*
* @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com)
*/
final class VariableStateSnapshot {
/**
* Inside class to save a variable-value pair
*
* @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com)
*/
private final static class VariableContainer {
/**
* The variable contains the value
*/
final Var variable;
/**
* The variable contains the etalon value for the variable
*/
final Term etalonValue;
/**
* The constructor
*
* @param var the source var which one will be saved into the container
*/
public VariableContainer(final Var var) {
this.variable = var;
this.etalonValue = var.getThisValue();
}
/**
* Reset the variable's value to the etalon value
*/
public void resetToEtalon() {
this.variable.setThisValue(this.etalonValue);
}
/**
* Check that the variable's value is not equals to the etalon value
*
* @return true if the variable was changed, else false
*/
public boolean isChanged() {
return this.variable.getThisValue() != this.etalonValue;
}
}
/**
* The list of containers for variables
*/
private final List<VariableContainer> containers;
/**
* Inside temporary hashset to save uids of processed variables, it is being
* used only at the constructor
*/
private IntegerHashSet processedVariables;
/**
* A Constructor, to make new object based on values from other shapshot
*
* @param snapshot the snapshot whose variables will be used in the new one,
* must not be null
*/
public VariableStateSnapshot(final VariableStateSnapshot snapshot) {
this.containers = new ArrayList<VariableContainer>();
final List<VariableContainer> thatContainers = snapshot.containers;
final Iterator<VariableContainer> iterator = thatContainers.iterator();
List<VariableContainer> changed = null;
this.processedVariables = new IntegerHashSet();
while (iterator.hasNext()) {
final VariableContainer container = iterator.next();
if (container.isChanged()) {
if (changed == null) {
changed = new ArrayList<VariableContainer>(16);
}
changed.add(container);
}
else {
this.processedVariables.add(container.variable.getVarUID());
this.containers.add(container);
}
}
if (changed != null) {
final Iterator<VariableContainer> iter = changed.iterator();
while (iter.hasNext()) {
final VariableContainer container = iter.next();
final int uid = container.variable.getVarUID();
if (!this.processedVariables.contains(uid)) {
this.processedVariables.add(uid);
this.containers.add(new VariableContainer(container.variable));
extractAllVariables(container.variable.getThisValue());
}
}
}
this.processedVariables = null;
}
/**
* A constructor allows to make snapshot based on the term
*
* @param source the term whose variables will be saved at the snapshot, must
* not be null
*/
public VariableStateSnapshot(final Term source) {
this.containers = new ArrayList<VariableContainer>();
extractAllVariables(source);
this.processedVariables = null;
}
/**
* Function extracts all found variables from the term into inside storage
*
* @param src the source term to be processed
*/
private void extractAllVariables(final Term src) {
if (src == null) {
return;
}
switch (src.getTermType()) {
case Term.TYPE_LIST: {
final TermList list = (TermList) src;
if (!list.isNullList()) {
extractAllVariables(list.getHead());
extractAllVariables(list.getTail());
}
}
break;
case Term.TYPE_STRUCT: {
final TermStruct struct = (TermStruct) src;
final Term[] elements = struct.getElementsAsArray();
final int len = elements.length;
for (int li = 0; li < len; li++) {
extractAllVariables(elements[li]);
}
}
break;
case Term.TYPE_VAR: {
if (this.processedVariables == null) {
this.processedVariables = new IntegerHashSet();
}
final Var var = (Var) src;
final Integer uid = var.getVarUID();
if (!this.processedVariables.contains(uid)) {
this.processedVariables.add(uid);
this.containers.add(new VariableContainer(var));
final Term value = var.getThisValue();
if (value != null) {
extractAllVariables(value);
}
}
}
break;
}
}
/**
* Reset all saved variables to their saved original states
*/
public void resetToState() {
final Iterator<VariableContainer> iterator = this.containers.iterator();
while (iterator.hasNext()) {
iterator.next().resetToEtalon();
}
}
/**
* Get the inside variable storage size
*
* @return the number of variables saved in the snapshot
*/
public int getSize() {
return this.containers.size();
}
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
buffer.append(super.toString());
buffer.append('[');
final Iterator<VariableContainer> iter = this.containers.iterator();
boolean notfirst = false;
while (iter.hasNext()) {
final VariableContainer varcont = iter.next();
if (notfirst) {
buffer.append(',');
}
else {
notfirst = true;
}
final String valueTxt;
final Var value = varcont.variable;
if (value == null) {
valueTxt = ".NULL";
}
else {
if (value.getTermType() == Term.TYPE_VAR) {
if (value.isUndefined()) {
valueTxt = value.getSourceLikeRepresentation() + '{' + value.getVarUID() + '}';
}
else {
valueTxt = value.getSourceLikeRepresentation() + '{' + value.getVarUID() + '}' + '[' + value.getValue().toString() + ']';
}
}
else {
valueTxt = value.forWrite();
}
}
buffer.append(value == null ? valueTxt : value.getSourceLikeRepresentation()).append('{').append(value == null ? "<NULL>" : value.getVarUID()).append('}').append('=').append(valueTxt);
}
buffer.append(']');
return buffer.toString();
}
}