code: /*
   * citygml-tools - Collection of tools for processing CityGML files
   * https://github.com/citygml4j/citygml-tools
   *
   * citygml-tools is part of the citygml4j project
   *
   * Copyright 2018-2023 Claus Nagel <claus.nagel@gmail.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 org.citygml4j.tools.util;
  
  import org.citygml4j.core.model.cityobjectgroup.CityObjectGroup;
  import org.citygml4j.core.model.cityobjectgroup.RoleProperty;
  import org.citygml4j.core.model.core.AbstractCityObjectReference;
  import org.citygml4j.core.model.core.AbstractFeature;
  import org.citygml4j.core.visitor.ObjectWalker;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  public class CityObjectGroupRemover {
      private final List<CityObjectGroup> groups;
      private final Map<String, List<AbstractCityObjectReference>> groupParents = new HashMap<>();
      private final Map<String, List<RoleProperty>> groupMembers = new HashMap<>();
      private final MemberProcessor memberProcessor = new MemberProcessor();
  
      private CityObjectGroupRemover(List<CityObjectGroup> groups) {
          this.groups = groups;
      }
  
      public static CityObjectGroupRemover of(List<CityObjectGroup> groups) {
          CityObjectGroupRemover remover = new CityObjectGroupRemover(groups);
          remover.preprocess();
          return remover;
      }
  
      public List<CityObjectGroup> getCityObjectGroups() {
          return groups;
      }
  
      public boolean hasCityObjectGroups() {
          return !groups.isEmpty();
      }
  
      private void preprocess() {
          if (!groups.isEmpty()) {
              int capacity = Math.min(10, groups.size());
  
              for (CityObjectGroup group : groups) {
                  if (group.getGroupParent() != null && group.getGroupParent().getHref() != null) {
                      String id = CityObjects.getIdFromReference(group.getGroupParent().getHref());
                      groupParents.computeIfAbsent(id, v -> new ArrayList<>(capacity)).add(group.getGroupParent());
                  }
  
                  if (group.isSetGroupMembers()) {
                      for (RoleProperty property : group.getGroupMembers()) {
                          if (property.getObject() != null
                                  && property.getObject().getGroupMember() != null
                                  && property.getObject().getGroupMember().getHref() != null) {
                              String id = CityObjects.getIdFromReference(property.getObject().getGroupMember().getHref());
                              groupMembers.computeIfAbsent(id, v -> new ArrayList<>(capacity)).add(property);
                          }
                      }
                  }
              }
          }
      }
  
      public void removeMembers(AbstractFeature feature) {
          if (!groups.isEmpty()) {
              feature.accept(memberProcessor);
          }
      }
  
      public void postprocess() {
          if (!groups.isEmpty()) {
              groups.removeIf(group -> !group.isSetGroupMembers());
          }
      }
  
      private class MemberProcessor extends ObjectWalker {
  
          @Override
          public void visit(AbstractFeature feature) {
              if (feature.getId() != null) {
                  List<AbstractCityObjectReference> references = groupParents.remove(feature.getId());
                  if (references != null) {
                      references.forEach(reference -> reference.getParent(CityObjectGroup.class).setGroupParent(null));
                  }
  
                  List<RoleProperty> properties = groupMembers.remove(feature.getId());
                  if (properties != null) {
                      for (RoleProperty property : properties) {
                          CityObjectGroup group = property.getParent(CityObjectGroup.class);
                          group.getGroupMembers().remove(property);
                          if (!group.isSetGroupMembers()) {
                              visit((AbstractFeature) group);
                          }
                      }
                  }
              }
          }
      }
  }
  