org.apache.beehive.controls.runtime.generator.AptEventSet Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*
* $Header:$
*/
package org.apache.beehive.controls.runtime.generator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import com.sun.mirror.declaration.InterfaceDeclaration;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.declaration.TypeParameterDeclaration;
import com.sun.mirror.type.InterfaceType;
import org.apache.beehive.controls.api.events.EventSet;
import org.apache.beehive.controls.api.packaging.EventSetInfo;
import org.apache.beehive.controls.runtime.generator.apt.TwoPhaseAnnotationProcessor;
/**
* The AptEventSet class represents a control EventSet where the events
* are derived using APT metadata.
*/
public class AptEventSet extends AptType
{
/**
* Constructs a new AptEventSet instance from APT metadata
* @param controlIntf the declaring control interface
* @param eventSet the EventSet class
* @param ap the associated AnnotationProcessor
*/
public AptEventSet(AptControlInterface controlIntf, InterfaceDeclaration eventSet,
TwoPhaseAnnotationProcessor ap)
{
_controlIntf = controlIntf;
_eventSet = eventSet;
_ap = ap;
setDeclaration(eventSet);
EventSet eventSetAnnot = eventSet.getAnnotation(EventSet.class);
if (eventSetAnnot != null)
_unicast = eventSetAnnot.unicast();
//
// If an EventSet interface has formal type parameters, they must be a subset of
// the original formal type parameters declared on the original control interface.
// This is required because it must be possible to bind the types of events immediately
// upon construction of the bean... there is no opportunity to separately specify
// parameterization for the event set for the purpose of creating listeners, client
// notifiers, etc.
//
TypeDeclaration intfDecl = controlIntf.getTypeDeclaration();
for (TypeParameterDeclaration estpd : _eventSet.getFormalTypeParameters())
{
boolean found = false;
for (TypeParameterDeclaration citpd : intfDecl.getFormalTypeParameters())
{
if (estpd.getSimpleName().equals(citpd.getSimpleName()))
{
found = true;
break;
}
}
if (! found)
{
//
// BUGBUG: Ideally, this would be estpd.getPosition, but this seems to return
// 0,0 for the current APT implementation, so we use the event set position
// instead.
// Once this works, the 'break' below can also be removed to present errors
// for multiple invalid parameters
//
_ap.printError( eventSet, "eventset.formal.parameter.mismatch" );
break;
}
}
_superEventSet = initSuperEventSet();
_events = initEvents();
}
/**
* Checks to see if this EventSet extends an EventSet declared on a parent control interface. If
* found it will return the parent EventSet, or return null if not found.
*/
public AptEventSet initSuperEventSet()
{
// This will be common, so short circuit quickly
AptControlInterface superControl = _controlIntf.getSuperClass();
if (superControl == null)
return null;
// Compute a hash set containing the qualified names of all super interfaces
// for this EventSet
HashSet extendNames = new HashSet();
for (InterfaceType superType: _eventSet.getSuperinterfaces())
{
InterfaceDeclaration superDecl = superType.getDeclaration();
if (superDecl != null)
extendNames.add(superDecl.getQualifiedName());
}
// Starting with the parent of the ControlInterface declaring this EventSet, look
// for a parent interface that declares ones of these super interfaces as an event
// set
while (superControl != null)
{
Collection superEventSets = superControl.getEventSets();
for (AptEventSet superEventSet : superEventSets)
{
if (extendNames.contains(superEventSet.getClassName()))
return superEventSet;
}
superControl = superControl.getSuperClass();
}
// Nothing found, so no super event set
return null;
}
/**
* Returns any EventSet from which this event set derives (or null if none)
*/
public AptEventSet getSuperEventSet() { return _superEventSet; }
/**
* Initializes the list of Events associated with this EventSet
*/
protected AptMethodSet initEvents()
{
AptMethodSet events = new AptMethodSet();
if ( _eventSet == null || _eventSet.getMethods() == null )
return events;
//
// Add all of the public methods directly declared and inherited from extended
// interfaces, except for the EventSet super interface (if any)
//
ArrayList intfList = new ArrayList();
intfList.add(_eventSet);
for (int i = 0; i < intfList.size(); i++)
{
InterfaceDeclaration intfDecl = intfList.get(i);
//
// Don't add events that are derived from a super event set. These are not added because
// this class picks a single super interface to extend from when building a hierarchy
// of callback notifiers (etc). So, the super event set that was chosen first is left out
// of the list of event methods since they're captured in superclasses in the Control's implementation
//
if (_superEventSet != null && _superEventSet.getClassName().equals(intfDecl.getQualifiedName()))
continue;
// Add all declared methods, but ignore the mystery methods
for (MethodDeclaration methodDecl : intfDecl.getMethods())
if (!methodDecl.toString().equals("()"))
events.add(new AptEvent(this, methodDecl, _ap));
//
// Add all superinterfaces of the target interface to the list
//
for (InterfaceType superType: intfDecl.getSuperinterfaces())
{
InterfaceDeclaration superDecl = superType.getDeclaration();
if (superDecl != null && !intfList.contains(superDecl))
intfList.add(superDecl);
}
}
return events;
}
/**
* Returns the list of Events associated with this EventSet
*/
public Collection getEvents() { return _events.getMethods(); }
/**
* Returns 'true' if the event set support only unicast (single listener) events,
* false otherwise.
*/
public boolean isUnicast()
{
return _unicast;
}
/**
* Returns the number of Events for this EventSet and any super event set
*/
public int getEventCount()
{
int count = _events.size();
if (_superEventSet != null)
count += _superEventSet.getEventCount();
return count;
}
/**
* Returns the programmatic descriptor name to be returned by the EventDescriptor
* for the event set.
*/
public String getDescriptorName()
{
//
// The javadocs for java.beans.EventSetDescriptor suggest that the programmatic name
// should start w/ a lowercase letter. So we use the unqualified event set interface
// name w/ the first character lowercased.
//
String name = getShortName();
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
}
/**
* Returns the name of the generated notifier class for this ControlEventSet
*/
public String getNotifierClass()
{
StringBuffer sb = new StringBuffer(getShortName());
sb.append("Notifier");
//
// If the event set declaration has any parameterized types, then include them on
// the notifier class as well. Currently, these can only be parameterized types
// from the outer (control interface), since there is no other mechanism for specifying
// type values at notifier construction (other than propagation from the outer type).
//
sb.append(getFormalTypeParameterNames());
return sb.toString();
}
/**
* Returns any 'extends' clause that should be placed on the generated notifier class
*/
public String getNotifierExtends()
{
//
// All EventNotifiers are rooted from a common utility class, so if there is no
// super event set, then extend the utility notifier class.
//
if (_superEventSet == null)
{
if (_unicast)
return "org.apache.beehive.controls.runtime.bean.UnicastEventNotifier";
else
return "org.apache.beehive.controls.runtime.bean.EventNotifier";
}
//
// Otherwise, a generated notifier will extend the notifier of any parent event set
//
return _superEventSet.getNotifierClass();
}
/**
* Returns the short name for this notifier's base class.
*/
public String getNotifierExtendsShortName() {
if (_superEventSet == null)
{
if (_unicast)
return "UnicastEventNotifier";
else
return "EventNotifier";
}
return _superEventSet.getNotifierClass();
}
/**
* Return true if this notifier extends the UnicastEventNotifier or EventNotifier base class.
*/
public boolean isExtendsNotifierBase() {
return _superEventSet == null;
}
/**
* Returns the name of the method used to register a new EventSet listener
*/
public String getAddListenerMethod()
{
return "add" + getShortName() + "Listener";
}
/**
* Returns the name of the method used to register a new EventSet listener
*/
public String getRemoveListenerMethod()
{
return "remove" + getShortName() + "Listener";
}
/**
* Returns the name of the method used to retrieve the (unicast) EventSet listener
*/
public String getGetListenersMethod()
{
return "get" + getShortName() + "Listeners";
}
/**
* Returns the name of a custom-generated method to initialize MethodDescriptor bean
* info for the events in this EventSet
*/
public String getInfoInitializer()
{
return "init" + getShortName() + "Events";
}
/**
* Returns any EventSetInfo associated with the event set (or null if none)
*/
public EventSetInfo getEventSetInfo()
{
if ( _eventSet == null )
return null;
return _eventSet.getAnnotation(EventSetInfo.class);
}
/**
* Returns the underlying APT InterfaceDeclaration associated with this event set
*/
public InterfaceDeclaration getDeclaration()
{
return _eventSet;
}
private TwoPhaseAnnotationProcessor _ap;
private InterfaceDeclaration _eventSet;
private AptEventSet _superEventSet;
private AptControlInterface _controlIntf;
private AptMethodSet _events;
private boolean _unicast;
}