/* * Copyright (c) 2012 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eike Stepper - initial API and implementation */ package org.eclipse.emf.cdo.util; import org.eclipse.emf.cdo.common.util.CDOException; import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.emf.cdo.eresource.CDOResourceFolder; import org.eclipse.emf.cdo.eresource.CDOResourceNode; import org.eclipse.net4j.util.io.IOUtil; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import java.util.LinkedList; import java.util.Queue; /** * A utility class that attaches {@link EObject objects} to a {@link CDOResourceFolder folder} rooted, balanced tree such that configurable * capacities for resources per folder and objects per resource are never exceeded. This class is useful if a large number of objects * does not form a tree naturally but long lists would hinder fast lazy loading. * * @author Eike Stepper * @since 4.1 */ public class CDOBalancedTree { public static final int DEFAULT_CAPACITY = 20; public static final int DEFAULT_LOCK_TIMEOUT = 1000; private static final boolean TRACE = false; private final CDOResourceFolder root; private final int folderCapacity; private final int resourceCapacity; private int lockAttempts; private long lockTimeout = DEFAULT_LOCK_TIMEOUT; public CDOBalancedTree(CDOResourceFolder root, int folderCapacity, int resourceCapacity) { this.root = root; this.folderCapacity = folderCapacity; this.resourceCapacity = resourceCapacity; } public CDOBalancedTree(CDOResourceFolder root, int nodeCapacity) { this(root, nodeCapacity, nodeCapacity); } public CDOBalancedTree(CDOResourceFolder root) { this(root, DEFAULT_CAPACITY); } public final CDOResourceFolder getRoot() { return root; } public final int getFolderCapacity() { return folderCapacity; } public final int getResourceCapacity() { return resourceCapacity; } public final int getLockAttempts() { return lockAttempts; } public final void setLockAttempts(int lockAttempts) { this.lockAttempts = lockAttempts; } public final long getLockTimeout() { return lockTimeout; } public final void setLockTimeout(long lockTimeout) { this.lockTimeout = lockTimeout; } public void addObject(EObject object) { if (lockAttempts == 0) { addObjectToRoot(object); return; } int attempts = lockAttempts; while (attempts-- != 0) { try { root.cdoWriteLock().lock(lockTimeout); addObjectToRoot(object); return; } catch (Exception ex) { // Try again, if not all attempts have been made } } throw new CDOException("Unable to aquire write lock on balanced tree " + root.getPath()); } private void addObjectToRoot(EObject object) { CDOResource firstResource = null; Queue<CDOResourceFolder> folders = new LinkedList<CDOResourceFolder>(); CDOResourceFolder folder = root; while (folder != null) { EList<CDOResourceNode> nodes = folder.getNodes(); for (CDOResourceNode node : nodes) { if (node instanceof CDOResourceFolder) { folders.offer((CDOResourceFolder)node); } else if (node instanceof CDOResource) { if (firstResource == null) { firstResource = (CDOResource)node; } if (addObjectToResource(object, (CDOResource)node)) { return; } } } int size = nodes.size(); if (size < folderCapacity) { String name = getResourceName(size + 1); CDOResource resource = folder.addResource(name); if (TRACE) { IOUtil.OUT().println("Added resource " + resource.getPath()); } addObjectToResource(object, resource); return; } folder = folders.poll(); } CDOResource resource = addObjectWithSplit(firstResource); addObjectToResource(object, resource); } private boolean addObjectToResource(EObject object, CDOResource resource) { EList<EObject> contents = resource.getContents(); if (contents.size() < resourceCapacity) { contents.add(object); if (TRACE) { IOUtil.OUT().println("Added object to resource " + resource.getPath()); } return true; } return false; } private CDOResource addObjectWithSplit(CDOResource resource) { String path = resource.getPath(); String name = resource.getName(); resource.setName("_" + name); CDOResourceFolder splitFolder = resource.getFolder().addResourceFolder(name); splitFolder.getNodes().add(resource); resource.setName(getResourceName(1)); if (TRACE) { IOUtil.OUT().println("Moved resource " + path + " to " + resource.getPath()); } return splitFolder.addResource(getResourceName(2)); } private String getResourceName(int n) { return Integer.toString(n); } }