/*
* 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.
*/
package org.apache.sling.ide.transport.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.apache.sling.ide.transport.Batcher;
import org.apache.sling.ide.transport.Command;
import org.apache.sling.ide.util.PathUtil;
public class DefaultBatcher implements Batcher {
private List<Command<?>> queue = new ArrayList<>();
@Override
public void add(Command<?> command) {
queue.add(command);
}
@Override
public List<Command<?>> get() {
LinkedCommands batched = new LinkedCommands();
List<Command<?>> result = new ArrayList<>();
for ( Command<?> cmd : queue) {
boolean accepted = batched.addLinked(cmd);
if ( !accepted) {
result.add(cmd);
}
}
result.addAll(0, batched.getReorders());
result.addAll(0, batched.getUpdates());
result.addAll(0, batched.getDeletes());
// Expected order is:
// - delete
// - add-or-update
// - reorder
// - everything else, in the order it was specified
queue.clear();
return result;
}
private static class LinkedCommands {
private List<Command<?>> deletes = new ArrayList<>();
private List<Command<?>> updates = new ArrayList<>();
private List<Command<?>> reorders = new ArrayList<>();
public boolean addLinked(Command<?> newCmd) {
if ( newCmd.getKind() == null ) {
return false;
}
switch ( newCmd.getKind() ) {
case DELETE:
processDelete(newCmd);
return true;
case ADD_OR_UPDATE:
processWithPathEqualityCheck(newCmd, updates);
return true;
case REORDER_CHILDREN:
processWithPathEqualityCheck(newCmd, reorders);
return true;
default:
return false;
}
}
private void processDelete(Command<?> newCmd) {
String path = newCmd.getPath();
for ( ListIterator<Command<?>> iterator = deletes.listIterator(); iterator.hasNext(); ) {
// if we already have an ancestor deleted, skip this one
Command<?> oldCmd = iterator.next();
if ( PathUtil.isAncestor(oldCmd.getPath(), path ) ) {
return;
}
// if we are delete an ancestor of another resource which gets deleted, replace it
if ( PathUtil.isAncestor(path, oldCmd.getPath())) {
iterator.set(newCmd);
return;
}
}
// no matching deletions, add it as-is
deletes.add(newCmd);
}
private void processWithPathEqualityCheck(Command<?> newCmd, List<Command<?>> oldCmds) {
String path = newCmd.getPath();
for (Command<?> oldCmd : oldCmds) {
// if we already have an add-or-update for this path, skip it
if ( path.equals(oldCmd.getPath()) ) {
return;
}
}
// no adds or updates, add it as-is
oldCmds.add(newCmd);
}
public List<Command<?>> getDeletes() {
return deletes;
}
public List<Command<?>> getUpdates() {
return updates;
}
public List<Command<?>> getReorders() {
return reorders;
}
}
}