| /*=============================================================================# |
| # Copyright (c) 2017, 2020 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.rj.server.rh; |
| |
| import static org.eclipse.statet.rj.server.rh.ObjectManager.NO_REF; |
| import static org.eclipse.statet.rj.server.rh.ObjectManager.STRONG_REF; |
| import static org.eclipse.statet.rj.server.rh.ObjectManager.WEAK_REF; |
| |
| import java.util.Collection; |
| import java.util.IdentityHashMap; |
| import java.util.Set; |
| |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| |
| |
| /** |
| * R environment. |
| */ |
| @NonNullByDefault |
| public final class RhEnv { |
| |
| |
| private static final Object NO_DATA= new Object(); |
| |
| |
| public final Handle handle; |
| |
| private boolean isDisposed; |
| |
| private final RhEngine engine; |
| |
| private final @Nullable RhRefListener refListener; |
| private int strongCounter; |
| private int weakCounter; |
| private @Nullable RhWeakRef weakRef; |
| |
| |
| private final IdentityHashMap<String, @Nullable Object> regKeys= new IdentityHashMap<>(4); |
| |
| |
| public RhEnv(final RhEngine engine, final Handle handle, |
| final @Nullable RhRefListener refListener) { |
| this.engine= engine; |
| this.handle= handle; |
| this.refListener= refListener; |
| } |
| |
| |
| public boolean isRegAny() { |
| return (this.regKeys.size() > 0); |
| } |
| |
| public boolean isReg(final String key) { |
| return this.regKeys.containsKey(key); |
| } |
| |
| public boolean isRegAny(final Collection<String> keys) { |
| final Set<String> keySet= this.regKeys.keySet(); |
| for (final String key : keys) { |
| if (keySet.contains(key)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| public boolean addReg(final String key) { |
| if (!this.regKeys.containsKey(key)) { |
| this.regKeys.put(key, NO_DATA); |
| |
| if (!this.isDisposed) { |
| switch (key.charAt(1)) { |
| case STRONG_REF: |
| preserveStrong(); |
| break; |
| case WEAK_REF: |
| preserveWeak(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| |
| public boolean addReg(final String key, final Object data) { |
| if (this.regKeys.put(key, data) == null) { |
| if (!this.isDisposed) { |
| switch (key.charAt(1)) { |
| case STRONG_REF: |
| preserveStrong(); |
| break; |
| case WEAK_REF: |
| preserveWeak(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| |
| public @Nullable Object getData(final String key) { |
| final Object object= this.regKeys.get(key); |
| return (object != NO_DATA) ? object : null; |
| } |
| |
| public boolean removeReg(final String key) { |
| if (this.regKeys.remove(key) != null) { |
| |
| if (!this.isDisposed) { |
| switch (key.charAt(1)) { |
| case STRONG_REF: |
| releaseStrong(); |
| break; |
| case WEAK_REF: |
| releaseWeak(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| |
| |
| private void checkWeakRef() { |
| if (this.weakCounter > 0 && this.strongCounter == 0 && this.weakRef == null) { |
| this.weakRef= this.engine.newWeakRef(this.handle, (this.refListener != null) ? |
| this.refListener : |
| (final RhRef ref) -> dispose() ); |
| } |
| } |
| |
| private void preserveStrong() { |
| if (this.strongCounter++ == 0) { |
| this.engine.preserve(this.handle); |
| } |
| } |
| |
| private void releaseStrong() { |
| if (--this.strongCounter == 0) { |
| checkWeakRef(); |
| this.engine.releasePreserved(this.handle); |
| } |
| } |
| |
| private void preserveWeak() { |
| if (this.weakCounter++ == 0) { |
| checkWeakRef(); |
| } |
| } |
| |
| private void releaseWeak() { |
| --this.weakCounter; |
| } |
| |
| |
| public boolean isDisposed() { |
| return this.isDisposed; |
| } |
| |
| protected void dispose() { |
| if (this.isDisposed) { |
| return; |
| } |
| |
| this.isDisposed= true; |
| if (this.weakRef != null) { |
| this.weakRef.dispose(this.engine); |
| } |
| if (this.strongCounter > 0) { |
| this.strongCounter= 0; |
| this.engine.releasePreserved(this.handle); |
| } |
| } |
| |
| |
| @Override |
| public String toString() { |
| final StringBuilder sb= new StringBuilder("env "); |
| sb.append(this.handle.toString()); |
| sb.append(" ("); |
| if (this.isDisposed) { |
| sb.append("disposed, "); |
| } |
| if (this.strongCounter > 0) { |
| sb.append("r= " + STRONG_REF); |
| } |
| else if (this.weakRef != null) { |
| sb.append("r= " + WEAK_REF); |
| } |
| else { |
| sb.append("r= " + NO_REF); |
| } |
| sb.append(')'); |
| return sb.toString(); |
| } |
| |
| } |