blob: 6d3dd600bbe2e255ea804c85da85f6b960f3bdab [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// 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 com.google.gerrit.client.api;
import com.google.gerrit.client.GerritUiExtensionPoint;
import com.google.gerrit.client.rpc.Natives;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ExtensionPanel extends FlowPanel {
private static final Logger logger = Logger.getLogger(ExtensionPanel.class.getName());
private final GerritUiExtensionPoint extensionPoint;
private final List<Context> contexts;
public ExtensionPanel(GerritUiExtensionPoint extensionPoint) {
this(extensionPoint, new ArrayList<String>());
}
public ExtensionPanel(GerritUiExtensionPoint extensionPoint, List<String> panelNames) {
this.extensionPoint = extensionPoint;
this.contexts = create(panelNames);
}
private List<Context> create(List<String> panelNames) {
List<Context> contexts = new ArrayList<>();
for (Definition def : getOrderedDefs(panelNames)) {
SimplePanel p = new SimplePanel();
add(p);
contexts.add(Context.create(def, p));
}
return contexts;
}
private List<Definition> getOrderedDefs(List<String> panelNames) {
if (panelNames == null) {
panelNames = Collections.emptyList();
}
Map<String, List<Definition>> defsOrderedByName = new LinkedHashMap<>();
for (String name : panelNames) {
defsOrderedByName.put(name, new ArrayList<Definition>());
}
for (Definition def : Natives.asList(Definition.get(extensionPoint.name()))) {
addDef(def, defsOrderedByName);
}
List<Definition> orderedDefs = new ArrayList<>();
for (List<Definition> defList : defsOrderedByName.values()) {
orderedDefs.addAll(defList);
}
return orderedDefs;
}
private static void addDef(Definition def, Map<String, List<Definition>> defsOrderedByName) {
String panelName = def.getPanelName();
if (panelName.equals(def.getPluginName() + ".undefined")) {
/* Handle a partially undefined panel name from the
javascript layer by generating a random panel name.
This maintains support for panels that do not provide a name. */
panelName =
def.getPluginName() + "." + Long.toHexString(Double.doubleToLongBits(Math.random()));
}
if (defsOrderedByName.containsKey(panelName)) {
defsOrderedByName.get(panelName).add(def);
} else if (defsOrderedByName.containsKey(def.getPluginName())) {
defsOrderedByName.get(def.getPluginName()).add(def);
} else {
defsOrderedByName.put(panelName, Collections.singletonList(def));
}
}
public void put(GerritUiExtensionPoint.Key key, String value) {
for (Context ctx : contexts) {
ctx.put(key.name(), value);
}
}
public void putInt(GerritUiExtensionPoint.Key key, int value) {
for (Context ctx : contexts) {
ctx.putInt(key.name(), value);
}
}
public void putBoolean(GerritUiExtensionPoint.Key key, boolean value) {
for (Context ctx : contexts) {
ctx.putBoolean(key.name(), value);
}
}
public void putObject(GerritUiExtensionPoint.Key key, JavaScriptObject value) {
for (Context ctx : contexts) {
ctx.putObject(key.name(), value);
}
}
@Override
protected void onLoad() {
super.onLoad();
for (Context ctx : contexts) {
try {
ctx.onLoad();
} catch (RuntimeException e) {
logger.log(
Level.SEVERE,
"Failed to load extension panel for extension point "
+ extensionPoint.name()
+ " from plugin "
+ ctx.getPluginName()
+ ": "
+ e.getMessage());
}
}
}
@Override
protected void onUnload() {
super.onUnload();
for (Context ctx : contexts) {
for (JavaScriptObject u : Natives.asList(ctx.unload())) {
ApiGlue.invoke(u);
}
}
}
static class Definition extends JavaScriptObject {
static final JavaScriptObject TYPE = init();
private static native JavaScriptObject init() /*-{
function PanelDefinition(n, c, x) {
this.pluginName = n;
this.onLoad = c;
this.name = x;
};
return PanelDefinition;
}-*/;
static native JsArray<Definition> get(String i) /*-{ return $wnd.Gerrit.panels[i] || [] }-*/;
protected Definition() {}
public final native String getPanelName() /*-{ return this.pluginName + "." + this.name; }-*/;
public final native String getPluginName() /*-{ return this.pluginName; }-*/;
}
static class Context extends JavaScriptObject {
static final Context create(Definition def, SimplePanel panel) {
return create(TYPE, def, panel.getElement());
}
final native void onLoad() /*-{ this._d.onLoad(this) }-*/;
final native JsArray<JavaScriptObject> unload() /*-{ return this._u }-*/;
final native String getPluginName() /*-{ return this._d.pluginName; }-*/;
final native void put(String k, String v) /*-{ this.p[k] = v; }-*/;
final native void putInt(String k, int v) /*-{ this.p[k] = v; }-*/;
final native void putBoolean(String k, boolean v) /*-{ this.p[k] = v; }-*/;
final native void putObject(String k, JavaScriptObject v) /*-{ this.p[k] = v; }-*/;
private static native Context create(JavaScriptObject T, Definition d, Element e)
/*-{ return new T(d,e) }-*/ ;
private static final JavaScriptObject TYPE = init();
private static native JavaScriptObject init() /*-{
var T = function(d,e) {
this._d = d;
this._u = [];
this.body = e;
this.p = {};
};
T.prototype = {
onUnload: function(f){this._u.push(f)},
};
return T;
}-*/;
protected Context() {}
}
}