2
0
mirror of https://github.com/HMCL-dev/HMCL.git synced 2025-04-24 18:50:52 +08:00

fixed crashing when removing mods

This commit is contained in:
huangyuhui 2015-12-29 20:35:40 +08:00
parent e3f0254736
commit 3471b4fec7
20 changed files with 152 additions and 243 deletions

@ -1,7 +1,7 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@ -38,5 +38,5 @@ public abstract class IMinecraftModService extends IMinecraftService {
public abstract boolean addMod(File f);
public abstract void removeMod(int[] index);
public abstract void removeMod(Object[] mods);
}

@ -1,7 +1,7 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@ -88,6 +88,11 @@ public class ModInfo implements Comparable<ModInfo> {
return location.hashCode();
}
@Override
public String toString() {
return getFileName();
}
public String getFileName() {
String n = location.getName();
return FileUtils.removeExtension(isActive() ? n : n.substring(0, n.length() - ".disabled".length()));
@ -107,8 +112,8 @@ public class ModInfo implements Comparable<ModInfo> {
ModInfo i = new ModInfo();
i.location = f;
List<ModInfo> m = C.gson.fromJson(new InputStreamReader(jar.getInputStream(entry)),
new TypeToken<List<ModInfo>>() {
}.getType());
new TypeToken<List<ModInfo>>() {
}.getType());
if (m != null && m.size() > 0) {
i = m.get(0);
i.location = f;
@ -118,8 +123,9 @@ public class ModInfo implements Comparable<ModInfo> {
private static ModInfo getLiteLoaderModInfo(File f, ZipFile jar, ZipEntry entry) throws IOException {
ModInfo m = C.gson.fromJson(new InputStreamReader(jar.getInputStream(entry)),
ModInfo.class);
if (m == null) m = new ModInfo();
ModInfo.class);
if (m == null)
m = new ModInfo();
m.location = f;
return m;
}

@ -104,13 +104,14 @@ public class MinecraftModService extends IMinecraftModService {
}
@Override
public void removeMod(int[] rows) {
Arrays.sort(rows);
for (int idx : rows) {
ModInfo mi = getMods().get(idx);
File f = mi.location;
f.delete();
}
public void removeMod(Object[] rows) {
if (rows.length == 0)
return;
for (Object r : rows)
if (r instanceof ModInfo)
((ModInfo) r).location.delete();
else if (r instanceof Number)
getMods().get(((Number) r).intValue()).location.delete();
recacheMods();
}

@ -615,9 +615,6 @@
<TableColumnModel selectionModel="1"/>
</Property>
<Property name="columnSelectionAllowed" type="boolean" value="true"/>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="0"/>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="false" resizingAllowed="true"/>
</Property>

@ -610,7 +610,6 @@ public final class GameSettingsPanel extends AnimatedPanel implements DropTarget
lstExternalMods.setModel(SwingUtils.makeDefaultTableModel(new String[]{"", "Mod", C.i18n("ui.label.version")}, new Class[]{Boolean.class,String.class,String.class}, new boolean[]{true,false,false}));
lstExternalMods.setColumnSelectionAllowed(true);
lstExternalMods.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
lstExternalMods.getTableHeader().setReorderingAllowed(false);
lstExternalMods.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
@ -1069,7 +1068,7 @@ public final class GameSettingsPanel extends AnimatedPanel implements DropTarget
}//GEN-LAST:event_btnAddModActionPerformed
private void btnRemoveModActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRemoveModActionPerformed
getProfile().getMinecraftProvider().getModService().removeMod(lstExternalMods.getSelectedRows());
getProfile().getMinecraftProvider().getModService().removeMod(SwingUtils.getValueBySelectedRow(lstExternalMods, lstExternalMods.getSelectedRows(), 1));
reloadMods();
}//GEN-LAST:event_btnRemoveModActionPerformed
@ -1248,12 +1247,13 @@ public final class GameSettingsPanel extends AnimatedPanel implements DropTarget
reloadingMods = true;
DefaultTableModel model = SwingUtils.clearDefaultTable(lstExternalMods);
Observable.<List<ModInfo>>createWithEmptySubscription(
t -> t.onNext(getProfile().getMinecraftProvider().getModService().recacheMods()))
.subscribeOn(Schedulers.newThread()).observeOn(Schedulers.eventQueue())
.flatMap(t -> Observable.from(t))
.subscribe(t -> model.addRow(new Object[] {t.isActive(), t.getFileName(), t.version}),
null,
() -> reloadingMods = false);
t -> t.onNext(getProfile().getMinecraftProvider().getModService().recacheMods()))
.subscribeOn(Schedulers.newThread()).observeOn(Schedulers.eventQueue())
.subscribe(t -> {
for (ModInfo x : t)
model.addRow(new Object[]{x.isActive(), x, x.version});
reloadingMods = false;
});
}
// </editor-fold>

@ -139,7 +139,7 @@ public final class MainFrame extends DraggableFrame {
if (enableShadow)
try {
//setBackground(new Color(0, 0, 0, 0));
setBackground(new Color(0, 0, 0, 0));
getRootPane().setBorder(border = new DropShadowBorder(borderColor, 4));
} catch (Throwable ex) {
HMCLog.err("Failed to set window transparent.", ex);

@ -1,7 +1,7 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@ -42,16 +42,16 @@ public class SwingUtils {
* Make DefaultTableModel by overriding getColumnClass and isCellEditable of
* DefaultTableModel.
*
* @param titleA The title of each column.
* @param typesA The type of each column value.
* @param titleA The title of each column.
* @param typesA The type of each column value.
* @param canEditA Is column editable?
*
* @return
*/
public static DefaultTableModel makeDefaultTableModel(String[] titleA, final Class[] typesA, final boolean[] canEditA) {
return new DefaultTableModel(
new Object[][] {},
titleA) {
new Object[][]{},
titleA) {
Class[] types = typesA;
boolean[] canEdit = canEditA;
@ -114,7 +114,7 @@ public class SwingUtils {
/**
* Append new element to JList
*
* @param list the JList
* @param list the JList
* @param element the Element
*/
public static void appendLast(JList list, Object element) {
@ -154,6 +154,14 @@ public class SwingUtils {
model.setValueAt(element, row, col);
}
public static Object[] getValueBySelectedRow(JTable table, int rows[], int col) {
DefaultTableModel model = (DefaultTableModel) table.getModel();
Object[] ret = new Object[rows.length];
for (int i = 0; i < rows.length; i++)
ret[i] = model.getValueAt(rows[i], col);
return ret;
}
public static void removeRow(JTable table, int row) {
DefaultTableModel model = (DefaultTableModel) table.getModel();
model.removeRow(row);

@ -1,12 +1,12 @@
/**
* Copyright 2013 Netflix, Inc.
*
*
* 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.
@ -17,8 +17,9 @@ package rx;
/**
* An object representing a notification sent to an {@link Observable}.
*
* For the Microsoft Rx equivalent see: http://msdn.microsoft.com/en-us/library/hh229462(v=vs.103).aspx
*
* For the Microsoft Rx equivalent see:
* http://msdn.microsoft.com/en-us/library/hh229462(v=vs.103).aspx
*/
public class Notification<T> {
@ -28,9 +29,8 @@ public class Notification<T> {
/**
* A constructor used to represent an onNext notification.
*
* @param value
* The data passed to the onNext method.
*
* @param value The data passed to the onNext method.
*/
public Notification(T value) {
this.value = value;
@ -40,9 +40,8 @@ public class Notification<T> {
/**
* A constructor used to represent an onError notification.
*
* @param exception
* The exception passed to the onError notification.
*
* @param exception The exception passed to the onError notification.
*/
public Notification(Exception exception) {
this.exception = exception;
@ -61,7 +60,7 @@ public class Notification<T> {
/**
* Retrieves the exception associated with an onError notification.
*
*
* @return The exception associated with an onError notification.
*/
public Exception getException() {
@ -70,7 +69,7 @@ public class Notification<T> {
/**
* Retrieves the data associated with an onNext notification.
*
*
* @return The data associated with an onNext notification.
*/
public T getValue() {
@ -79,7 +78,7 @@ public class Notification<T> {
/**
* Retrieves a value indicating whether this notification has a value.
*
*
* @return a value indicating whether this notification has a value.
*/
public boolean hasValue() {
@ -88,7 +87,7 @@ public class Notification<T> {
/**
* Retrieves a value indicating whether this notification has an exception.
*
*
* @return a value indicating whether this notification has an exception.
*/
public boolean hasException() {
@ -97,7 +96,7 @@ public class Notification<T> {
/**
* The kind of notification: OnNext, OnError, OnCompleted
*
*
* @return
*/
public Kind getKind() {
@ -150,12 +149,8 @@ public class Notification<T> {
if (obj.getClass() != getClass())
return false;
Notification<?> notification = (Notification<?>) obj;
if (notification.getKind() != getKind())
if (notification.getKind() != getKind() || hasValue() && !getValue().equals(notification.getValue()))
return false;
if (hasValue() && !getValue().equals(notification.getValue()))
return false;
if (hasException() && !getException().equals(notification.getException()))
return false;
return true;
return !(hasException() && !getException().equals(notification.getException()));
}
}

@ -1,12 +1,12 @@
/**
* Copyright 2013 Netflix, Inc.
*
*
* 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.
@ -23,7 +23,8 @@ import rx.subscriptions.Subscriptions;
import rx.util.functions.Action0;
import rx.util.functions.Func0;
/* package */abstract class AbstractScheduler implements Scheduler {
/* package */
abstract class AbstractScheduler implements Scheduler {
@Override
public Subscription schedule(Action0 action) {
@ -41,12 +42,9 @@ import rx.util.functions.Func0;
}
private static Func0<Subscription> asFunc0(final Action0 action) {
return new Func0<Subscription>() {
@Override
public Subscription call() {
action.call();
return Subscriptions.empty();
}
return () -> {
action.call();
return Subscriptions.empty();
};
}

@ -1,12 +1,12 @@
/**
* Copyright 2013 Netflix, Inc.
*
*
* 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.
@ -27,26 +27,30 @@ import rx.Subscription;
import rx.util.functions.Func0;
/**
* A {@link Scheduler} implementation that uses an {@link Executor} or {@link ScheduledExecutorService} implementation.
* A {@link Scheduler} implementation that uses an {@link Executor} or
* {@link ScheduledExecutorService} implementation.
* <p>
* Note that if an {@link Executor} implementation is used instead of {@link ScheduledExecutorService} then a system-wide Timer will be used to handle delayed events.
* Note that if an {@link Executor} implementation is used instead of
* {@link ScheduledExecutorService} then a system-wide Timer will be used to
* handle delayed events.
*/
public class ExecutorScheduler extends AbstractScheduler {
private final Executor executor;
/**
* Setup a ScheduledExecutorService that we can use if someone provides an Executor instead of ScheduledExecutorService.
* Setup a ScheduledExecutorService that we can use if someone provides an
* Executor instead of ScheduledExecutorService.
*/
private final static ScheduledExecutorService SYSTEM_SCHEDULED_EXECUTOR;
static {
int count = Runtime.getRuntime().availableProcessors();
if (count > 8) {
if (count > 8)
count = count / 2;
}
// we don't need more than 8 to handle just scheduling and doing no work
if (count > 8) {
if (count > 8)
count = 8;
}
SYSTEM_SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(count, new ThreadFactory() {
final AtomicInteger counter = new AtomicInteger();
@ -74,37 +78,18 @@ public class ExecutorScheduler extends AbstractScheduler {
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
final DiscardableAction discardableAction = new DiscardableAction(action);
if (executor instanceof ScheduledExecutorService) {
((ScheduledExecutorService) executor).schedule(new Runnable() {
@Override
public void run() {
discardableAction.call();
}
if (executor instanceof ScheduledExecutorService)
((ScheduledExecutorService) executor).schedule(discardableAction::call, dueTime, unit);
else if (dueTime == 0)
// no delay so put on the thread-pool right now
return (schedule(action));
else
// there is a delay and this isn't a ScheduledExecutorService so we'll use a system-wide ScheduledExecutorService
// to handle the scheduling and once it's ready then execute on this Executor
SYSTEM_SCHEDULED_EXECUTOR.schedule(() -> {
// now execute on the real Executor
executor.execute(discardableAction::call);
}, dueTime, unit);
} else {
if (dueTime == 0) {
// no delay so put on the thread-pool right now
return (schedule(action));
} else {
// there is a delay and this isn't a ScheduledExecutorService so we'll use a system-wide ScheduledExecutorService
// to handle the scheduling and once it's ready then execute on this Executor
SYSTEM_SCHEDULED_EXECUTOR.schedule(new Runnable() {
@Override
public void run() {
// now execute on the real Executor
executor.execute(new Runnable() {
@Override
public void run() {
discardableAction.call();
}
});
}
}, dueTime, unit);
}
}
return discardableAction;
}
@ -112,12 +97,7 @@ public class ExecutorScheduler extends AbstractScheduler {
public Subscription schedule(Func0<Subscription> action) {
final DiscardableAction discardableAction = new DiscardableAction(action);
executor.execute(new Runnable() {
@Override
public void run() {
discardableAction.call();
}
});
executor.execute(discardableAction::call);
return discardableAction;

@ -1,19 +0,0 @@
/**
* Copyright 2013 Netflix, Inc.
*
* 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.
*/
/**
* Rx Schedulers
*/
package rx.concurrency;

@ -19,10 +19,8 @@ import rx.Observable;
import rx.Observer;
import rx.Scheduler;
import rx.Subscription;
import rx.util.functions.Action0;
import rx.util.functions.Func1;
public class OperationSubscribeOn {
public static <T> Func1<Observer<T>, Subscription> subscribeOn(Observable<T> source, Scheduler scheduler) {
@ -30,6 +28,7 @@ public class OperationSubscribeOn {
}
private static class SubscribeOn<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> source;
private final Scheduler scheduler;
@ -45,6 +44,7 @@ public class OperationSubscribeOn {
}
private static class ScheduledSubscription implements Subscription {
private final Subscription underlying;
private final Scheduler scheduler;
@ -59,4 +59,4 @@ public class OperationSubscribeOn {
}
}
}
}

@ -1,12 +1,12 @@
/**
* Copyright 2013 Netflix, Inc.
*
*
* 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.
@ -19,7 +19,9 @@ import rx.Observer;
import rx.Scheduler;
import rx.util.functions.Action0;
/* package */class ScheduledObserver<T> implements Observer<T> {
/* package */
class ScheduledObserver<T> implements Observer<T> {
private final Observer<T> underlying;
private final Scheduler scheduler;
@ -30,31 +32,20 @@ import rx.util.functions.Action0;
@Override
public void onCompleted() {
scheduler.schedule(new Action0() {
@Override
public void call() {
underlying.onCompleted();
}
scheduler.schedule(() -> {
underlying.onCompleted();
});
}
@Override
public void onError(final Exception e) {
scheduler.schedule(new Action0() {
@Override
public void call() {
underlying.onError(e);
}
});
scheduler.schedule(() -> underlying.onError(e));
}
@Override
public void onNext(final T args) {
scheduler.schedule(new Action0() {
@Override
public void call() {
underlying.onNext(args);
}
scheduler.schedule(() -> {
underlying.onNext(args);
});
}
}

@ -1,45 +0,0 @@
/**
* Copyright 2013 Netflix, Inc.
*
* 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.
*/
/**
* <p>Rx Observables</p>
*
* <p>A library that enables subscribing to and composing asynchronous events and
* callbacks.</p>
* <p>The Observable/Observer interfaces and associated operators (in
* the .operations package) are inspired by and attempt to conform to the
* Reactive Rx library in Microsoft .Net.</p>
* <p>
* More information can be found at <a
* href="http://msdn.microsoft.com/en-us/data/gg577609">http://msdn.microsoft.com/en-us/data/gg577609</a>.
* </p>
*
*
* <p>Compared with the Microsoft implementation:
* <ul>
* <li>Observable == IObservable</li>
* <li>Observer == IObserver</li>
* <li>Subscription == IDisposable</li>
* <li>ObservableExtensions == Observable</li>
* </ul>
* </p>
* <p>Services which intend on exposing data asynchronously and wish
* to allow reactive processing and composition can implement the
* Watchable interface which then allows Watchers to subscribe to them
* and receive events.</p>
* <p>Usage examples can be found on the Watchable and Watcher
* classes.</p>
*/
package rx;

@ -9,29 +9,24 @@ import rx.util.SynchronizedObserver;
import rx.util.functions.Func1;
public class Subject<T> extends Observable<T> implements Observer<T> {
public static <T> Subject<T> create() {
final ConcurrentHashMap<Subscription, Observer<T>> observers = new ConcurrentHashMap<Subscription, Observer<T>>();
final ConcurrentHashMap<Subscription, Observer<T>> observers = new ConcurrentHashMap<>();
Func1<Observer<T>, Subscription> onSubscribe = new Func1<Observer<T>, Subscription>() {
@Override
public Subscription call(Observer<T> observer) {
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
Func1<Observer<T>, Subscription> onSubscribe = observer -> {
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
subscription.wrap(new Subscription() {
@Override
public void unsubscribe() {
// on unsubscribe remove it from the map of outbound observers to notify
observers.remove(subscription);
}
});
subscription.wrap(() -> {
// on unsubscribe remove it from the map of outbound observers to notify
observers.remove(subscription);
});
// on subscribe add it to the map of outbound observers to notify
observers.put(subscription, new SynchronizedObserver<T>(observer, subscription));
return subscription;
}
// on subscribe add it to the map of outbound observers to notify
observers.put(subscription, new SynchronizedObserver<>(observer, subscription));
return subscription;
};
return new Subject<T>(onSubscribe, observers);
return new Subject<>(onSubscribe, observers);
}
private final ConcurrentHashMap<Subscription, Observer<T>> observers;
@ -43,23 +38,20 @@ public class Subject<T> extends Observable<T> implements Observer<T> {
@Override
public void onCompleted() {
for (Observer<T> observer : observers.values()) {
for (Observer<T> observer : observers.values())
observer.onCompleted();
}
}
@Override
public void onError(Exception e) {
for (Observer<T> observer : observers.values()) {
for (Observer<T> observer : observers.values())
observer.onError(e);
}
}
@Override
public void onNext(T args) {
for (Observer<T> observer : observers.values()) {
for (Observer<T> observer : observers.values())
observer.onNext(args);
}
}
}

@ -1,12 +1,12 @@
/**
* Copyright 2013 Netflix, Inc.
*
*
* 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.
@ -21,19 +21,22 @@ import java.util.concurrent.atomic.AtomicReference;
import rx.Subscription;
/**
* Thread-safe wrapper around Observable Subscription that ensures unsubscribe can be called only once.
* Thread-safe wrapper around Observable Subscription that ensures unsubscribe
* can be called only once.
* <p>
* Also used to:
* <p>
* <ul>
* <li>allow the AtomicObserver to have access to the subscription in asynchronous execution for checking if unsubscribed occurred without onComplete/onError.</li>
* <li>allow the AtomicObserver to have access to the subscription in
* asynchronous execution for checking if unsubscribed occurred without
* onComplete/onError.</li>
* <li>handle both synchronous and asynchronous subscribe() execution flows</li>
* </ul>
*/
public final class AtomicObservableSubscription implements Subscription {
private AtomicReference<Subscription> actualSubscription = new AtomicReference<Subscription>();
private AtomicBoolean unsubscribed = new AtomicBoolean(false);
private final AtomicReference<Subscription> actualSubscription = new AtomicReference<>();
private final AtomicBoolean unsubscribed = new AtomicBoolean(false);
public AtomicObservableSubscription() {
@ -44,16 +47,16 @@ public final class AtomicObservableSubscription implements Subscription {
}
/**
* Wraps the actual subscription once it exists (if it wasn't available when constructed)
*
* Wraps the actual subscription once it exists (if it wasn't available when
* constructed)
*
* @param actualSubscription
* @throws IllegalStateException
* if trying to set more than once (or use this method after setting via constructor)
* @throws IllegalStateException if trying to set more than once (or use
* this method after setting via constructor)
*/
public AtomicObservableSubscription wrap(Subscription actualSubscription) {
if (!this.actualSubscription.compareAndSet(null, actualSubscription)) {
if (!this.actualSubscription.compareAndSet(null, actualSubscription))
throw new IllegalStateException("Can not set subscription more than once.");
}
return this;
}

@ -1,12 +1,12 @@
/**
* Copyright 2013 Netflix, Inc.
*
*
* 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.
@ -23,7 +23,8 @@ import java.util.List;
/**
* Exception that is a composite of 1 or more other exceptions.
* <p>
* The <code>getMessage()</code> will return a concatenation of the composite exceptions.
* The <code>getMessage()</code> will return a concatenation of the composite
* exceptions.
*/
public class CompositeException extends RuntimeException {
@ -34,16 +35,14 @@ public class CompositeException extends RuntimeException {
public CompositeException(String messagePrefix, Collection<Exception> errors) {
StringBuilder _message = new StringBuilder();
if (messagePrefix != null) {
if (messagePrefix != null)
_message.append(messagePrefix).append(" => ");
}
List<Exception> _exceptions = new ArrayList<Exception>();
List<Exception> _exceptions = new ArrayList<>();
for (Exception e : errors) {
_exceptions.add(e);
if (_message.length() > 0) {
if (_message.length() > 0)
_message.append(", ");
}
_message.append(e.getClass().getSimpleName()).append(":").append(e.getMessage());
}
this.exceptions = Collections.unmodifiableList(_exceptions);
@ -62,4 +61,4 @@ public class CompositeException extends RuntimeException {
public String getMessage() {
return message;
}
}
}

@ -254,6 +254,7 @@ launcher.update_launcher=Check for update
launcher.enable_shadow=Enable Window Shadow
launcher.theme=Theme
launcher.proxy=Proxy
launcher.decorated=Enable system window border(in order to fix the problem that the ui become all gray in Linux OS)
launcher.title.game=Games
launcher.title.main=Home

@ -254,6 +254,7 @@ launcher.update_launcher=\u68c0\u67e5\u66f4\u65b0
launcher.enable_shadow=\u542f\u7528\u7a97\u53e3\u9634\u5f71(\u91cd\u542f\u542f\u52a8\u5668\u751f\u6548)
launcher.theme=\u4e3b\u9898
launcher.proxy=\u4ee3\u7406
launcher.decorated=\u555f\u7528\u7a97\u53e3\u908a\u6846(Linux\u4e0b\u53ef\u89e3\u6c7a\u7a0b\u5e8f\u754c\u9762\u5168\u7070\u554f\u984c)
launcher.title.game=\u904a\u6232\u8a2d\u5b9a
launcher.title.main=\u4e3b\u9801

@ -251,6 +251,7 @@ launcher.update_launcher=\u68c0\u67e5\u66f4\u65b0
launcher.enable_shadow=\u542f\u7528\u7a97\u53e3\u9634\u5f71(\u91cd\u542f\u542f\u52a8\u5668\u751f\u6548,\u53ef\u52a0\u5feb\u6e32\u67d3\u901f\u5ea6)
launcher.theme=\u4e3b\u9898
launcher.proxy=\u4ee3\u7406
launcher.decorated=\u542f\u7528\u7a97\u53e3\u8fb9\u6846(Linux\u4e0b\u53ef\u89e3\u51b3\u7a0b\u5e8f\u754c\u9762\u5168\u7070\u95ee\u9898)
launcher.title.game=\u6e38\u620f\u8bbe\u7f6e
launcher.title.main=\u4e3b\u9875