/*
* Copyright 2010 Sikiru.
*
* 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.
* under the License.
*/
package com.bramosystems.oss.player.core.rebind;
import com.bramosystems.oss.player.core.client.PlaylistSupport;
import com.bramosystems.oss.player.core.client.geom.MatrixSupport;
import com.bramosystems.oss.player.core.client.spi.Player;
import com.bramosystems.oss.player.core.client.spi.PlayerProvider;
import com.google.gwt.core.ext.*;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author Sikiru
*/
public class PlayerManagerGenerator extends Generator {
private final String DEFAULT_MIME_TYPES_FILE = "default-mime-types.properties";
private String className = null, packageName = null;
private TreeLogger logger;
private HashMap<String, _provider> pMap = new HashMap<String, _provider>();
private HashMap<String, String> mimeMap = new HashMap<String, String>();
public PlayerManagerGenerator() {
// init plugin props ...
}
@Override
public String generate(TreeLogger logger, GeneratorContext context, String typeName)
throws UnableToCompleteException {
this.logger = logger;
TypeOracle typeOracle = context.getTypeOracle();
try {
// get classType and save instance variables
JClassType classType = typeOracle.getType(typeName);
packageName = classType.getPackage().getName();
className = classType.getSimpleSourceName() + "Impl";
// Generate class source code
generateClass(logger, context);
} catch (Exception e) {
logger.log(TreeLogger.ERROR, "Unable to build Player Widgets!", e);
}
// return the fully qualifed name of the class generated
return packageName + "." + className;
}
/**
* @param logger Logger object
* @param context Generator context
*/
private void generateClass(TreeLogger logger, GeneratorContext context) throws
NotFoundException, BadPropertyValueException, UnableToCompleteException, IOException {
// get print writer that receives the source code
PrintWriter printWriter = context.tryCreate(logger, packageName, className);
// print writer if null, source code has ALREADY been generated, return
if (printWriter == null) {
return;
}
// build plugin mime types ...
ConfigurationProperty mimeFile =
context.getPropertyOracle().getConfigurationProperty("bstplayer.media.mimeTypes");
String val = mimeFile.getValues().get(0);
if (val == null) {
logger.log(TreeLogger.Type.INFO, "'" + mimeFile.getName() + "' configuration property not set! Using defaults");
parseMimeFile(DEFAULT_MIME_TYPES_FILE);
} else {
logger.log(TreeLogger.Type.INFO, "'" + mimeFile.getName() + "' set! Using '" + val + "'");
parseMimeFile(val);
}
collatePlayers(context.getTypeOracle());
// init composer, set class properties, create source writer
ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName, className);
composer.setSuperclass("PlayerManager");
composer.addImport("com.google.gwt.core.client.GWT");
composer.addImport("com.bramosystems.oss.player.core.client.spi.PlayerProviderFactory");
composer.addImport("com.bramosystems.oss.player.core.client.impl.CallbackUtility");
composer.addImport("com.bramosystems.oss.player.core.client.spi.ConfigurationContext");
composer.addImport("com.bramosystems.oss.player.core.client.*");
composer.addImport("java.util.*");
SourceWriter sourceWriter = composer.createSourceWriter(context, printWriter);
sourceWriter.println("private HashMap<String, HashMap<String, PlayerInfo>> pInfos = new HashMap<String, HashMap<String, PlayerInfo>>();");
// collate widget factories & create static holders ...
Iterator<String> fact = pMap.keySet().iterator();
while (fact.hasNext()) {
String provClass = fact.next();
sourceWriter.println("private static PlayerProviderFactory pwf_" + escapeProviderName(pMap.get(provClass).name)
+ " = GWT.create(" + provClass + ".class);");
}
sourceWriter.println();
Pattern ptrn = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)");
// generate constructor source code
sourceWriter.println("public " + className + "() { ");
sourceWriter.indent();
// init widget factories ...
fact = pMap.keySet().iterator();
while (fact.hasNext()) {
String provClass = fact.next();
String provName = escapeProviderName(pMap.get(provClass).name);
sourceWriter.println("pwf_" + provName + ".init(new ConfigurationContext(CallbackUtility.initCallbackHandlers(\""
+ provName + "\"), \"bstplayer.handlers." + provName + "\"));");
}
sourceWriter.println();
// init providers ...
fact = pMap.keySet().iterator();
while (fact.hasNext()) {
_provider pvd = pMap.get(fact.next());
sourceWriter.println("pInfos.put(\"" + pvd.name + "\", new HashMap<String, PlayerInfo>());");
Iterator<_player> pns = pvd.players.iterator();
while (pns.hasNext()) {
_player ply = pns.next();
boolean ps = false, ms = false;
JClassType ints[] = ply.interfaces;
for (int j = 0; j < ints.length; j++) {
if (ints[j].getQualifiedSourceName().equals(MatrixSupport.class.getName())) {
ms = true;
} else if (ints[j].getQualifiedSourceName().equals(PlaylistSupport.class.getName())) {
ps = true;
}
}
Matcher m = ptrn.matcher(ply.minPluginVer);
if (m.matches()) {
sourceWriter.println("pInfos.get(\"" + pvd.name + "\").put(\"" + ply.name
+ "\", new PlayerInfo(\"" + pvd.name + "\",\"" + ply.name + "\"," + "PluginVersion.get("
+ Integer.parseInt(m.group(1)) + "," + Integer.parseInt(m.group(2)) + "," + Integer.parseInt(m.group(3)) + "),"
+ ps + "," + ms + "));");
} else {
logger.log(TreeLogger.Type.WARN, "Min");
}
}
}
sourceWriter.outdent();
sourceWriter.println("}"); // end constructor source generation
sourceWriter.println();
// implement get provider names ...
sourceWriter.println("@Override");
sourceWriter.println("public Set<String> getProviders(){");
sourceWriter.indent();
sourceWriter.println("return new TreeSet<String>(pInfos.keySet());");
sourceWriter.outdent();
sourceWriter.println("}");
sourceWriter.println();
// implement get player names by provider ...
sourceWriter.println("@Override");
sourceWriter.println("public Set<String> getPlayerNames(String providerName) {");
sourceWriter.indent();
sourceWriter.println("if(!pInfos.containsKey(providerName))");
sourceWriter.println("throw new IllegalArgumentException(\"Unknown player provider - \" + providerName);");
sourceWriter.println("return new TreeSet<String>(pInfos.get(providerName).keySet());");
sourceWriter.outdent();
sourceWriter.println("}");
sourceWriter.println();
// implement get player infos ...
sourceWriter.println("@Override");
sourceWriter.println("public PlayerInfo getPlayerInfo(String providerName, String playerName) {");
sourceWriter.indent();
sourceWriter.println("if(!pInfos.containsKey(providerName)) {");
sourceWriter.println("throw new IllegalArgumentException(\"Unknown player provider - \" + providerName);}");
sourceWriter.println("if(providerName.equals(\"api\")){");
sourceWriter.println("return getApiPlayerInfoForPlugin(playerName);}");
sourceWriter.println("else if(!pInfos.get(providerName).containsKey(playerName)){");
sourceWriter.println("throw new IllegalArgumentException(\"Unknown player name - \" + playerName);}");
sourceWriter.println("return pInfos.get(providerName).get(playerName);");
sourceWriter.outdent();
sourceWriter.println("}");
sourceWriter.println();
// implement get widget factory with defered binding on demand ....
sourceWriter.println("@Override");
sourceWriter.println("public PlayerProviderFactory getProviderFactory(String provider) {");
sourceWriter.indent();
sourceWriter.println("PlayerProviderFactory wf = null;");
boolean firstRun = true;
Iterator<_provider> provs = pMap.values().iterator();
while (provs.hasNext()) {
_provider prov = provs.next();
if (firstRun) {
sourceWriter.println("if(\"" + prov.name + "\".equals(provider)) {");
} else {
sourceWriter.println("else if(\"" + prov.name + "\".equals(provider)) {");
}
sourceWriter.indent();
sourceWriter.println("wf = pwf_" + escapeProviderName(prov.name) + ";");
sourceWriter.outdent();
sourceWriter.println("}");
firstRun = false;
}
sourceWriter.println("return wf;");
sourceWriter.outdent();
sourceWriter.println("}");
sourceWriter.println();
// implement mimeTypes ....
sourceWriter.println("@Override");
sourceWriter.println("protected void initMimeTypes(HashMap<String, String> mimeTypes) {");
sourceWriter.indent();
Iterator<String> mimeKeys = mimeMap.keySet().iterator();
while (mimeKeys.hasNext()) {
String mime = mimeKeys.next();
sourceWriter.println("mimeTypes.put(\"" + mime + "\",\"" + mimeMap.get(mime) + "\");");
}
sourceWriter.outdent();
sourceWriter.println("}");
sourceWriter.println();
// close generated class
sourceWriter.outdent();
sourceWriter.println("}");
// commit generated class
context.commit(logger, printWriter);
}
private void collatePlayers(TypeOracle typeOracle) {
TreeLogger tl = logger.branch(TreeLogger.Type.INFO, "Searching for Player Providers");
JClassType a[] = typeOracle.getTypes();
for (int i = 0; i < a.length; i++) {
if (a[i].isAnnotationPresent(PlayerProvider.class)) {
String pName = a[i].getAnnotation(PlayerProvider.class).value();
tl.log(TreeLogger.Type.INFO, "Processing Player Provider : " + pName);
pMap.put(a[i].getQualifiedSourceName(), new _provider(pName));
}
}
tl = logger.branch(TreeLogger.Type.INFO, "Searching for Player widgets");
for (int i = 0; i < a.length; i++) {
if (a[i].isAnnotationPresent(Player.class)) {
Player p = a[i].getAnnotation(Player.class);
String pName = p.providerFactory().getName();
if (pMap.containsKey(pName)) {
tl.log(TreeLogger.Type.INFO, "Processing Player widget : " + a[i].getQualifiedSourceName());
_player _py = new _player(p.name(), p.minPluginVersion(), a[i].getQualifiedSourceName());
_py.interfaces = a[i].getImplementedInterfaces();
pMap.get(pName).players.add(_py);
} else {
logger.log(TreeLogger.Type.ERROR, "WidgetFactory '" + pName + "' should be annotated with @PlayerProvider");
}
}
}
}
private void parseMimeFile(String mimePropertyFile) throws IOException {
// build props ...
Properties p = new Properties();
p.load(getClass().getResourceAsStream(mimePropertyFile));
Iterator<String> types = p.stringPropertyNames().iterator();
while (types.hasNext()) {
String key = types.next();
if (key.toLowerCase().startsWith("audio") || key.toLowerCase().startsWith("video")) {
// mime types ...
mimeMap.put(key, p.getProperty(key));
}
}
}
// replace chars [.] with escaped strings ...
private String escapeProviderName(String provName) {
return provName.replace(".", "$");
}
private class _provider {
String name;
HashSet<_player> players = new HashSet<_player>();
public _provider(String name) {
this.name = name;
}
}
private class _player {
String name;
String minPluginVer;
String implClass;
JClassType[] interfaces;
public _player(String name, String minPluginVer, String implClass) {
this.name = name;
this.minPluginVer = minPluginVer;
this.implClass = implClass;
}
}
}