blob: 9f8dae9ed073e514a93329eaac7005a16c8f4a4c [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2008, 2014 Technical University Berlin, Germany and others
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Technical University Berlin - Initial API and implementation
* Stephan Herrmann - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.internal.osgi.weaving;
import static org.eclipse.objectteams.otequinox.TransformerPlugin.log;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.objectteams.otre.util.CallinBindingManager;
import org.osgi.framework.Bundle;
/**
* Simple facility for scanning class files for OT Attributes without
* actually doing any weaving.
*
* @author stephan
* @since 1.2.0
*/
@NonNullByDefault
public class ClassScanner
{
// default is "on", leave this switch for trouble shooting and profiling:
public static final boolean REPOSITORY_USE_RESOURCE_LOADER = !"off".equals(System.getProperty("otequinox.repository.hook"));
// collect class names recorded by readOTAttributes:
// * this version used by the MasterTeamLoader.loadTeams:
Map<String,ArrayList<String>> baseClassNamesByTeam = new HashMap<String, ArrayList<String>>();
// * these fields used by TransformerHook.processClass (collected over multiple readOTAttributes):
List<String> allBaseClassNames = new ArrayList<String>();
List<String> roleClassNames = new ArrayList<String>();
/**
* Read all OT byte code attributes for the specified class.
* While doing so the names of roles and adapted base classes are collected.
*
* @param bundle where to look
* @param className the class to investigate (team or role)
* @param loader the loader (could be null) to use for further classFile lookup
* @return the real class name (potentially involving '$' and '__OT__' substitution/insertion
* @throws ClassFormatError
* @throws IOException
* @throws ClassNotFoundException the team or role class was not found
*/
public String readOTAttributes(Bundle bundle, String className, DelegatingTransformer transformer)
throws ClassFormatError, IOException, ClassNotFoundException
{
Bundle loader = REPOSITORY_USE_RESOURCE_LOADER ? bundle : null;
Pair<URL,String> result = TeamLoader.findTeamClassResource(className, bundle);
if (result == null)
throw new ClassNotFoundException(className);
URL classFile = result.first;
className = result.second;
try (InputStream inputStream = classFile.openStream()) {
transformer.readOTAttributes(className, inputStream, classFile.getFile(), loader);
}
Collection<String> currentBaseNames = transformer.fetchAdaptedBases(); // destructive read
if (currentBaseNames != null) {
// store per team:
ArrayList<String> basesPerTeam = this.baseClassNamesByTeam.get(className);
if (basesPerTeam == null) {
basesPerTeam = new ArrayList<String>();
this.baseClassNamesByTeam.put(className, basesPerTeam);
}
basesPerTeam.addAll(currentBaseNames);
// accumulated store:
allBaseClassNames.addAll(currentBaseNames);
}
readMemberTypeAttributes(bundle, className, transformer);
return className;
}
/**
* Get the names of the base classes adapted by the given team and
* encountered while reading the byte code attributes.
* (Destructive read).
*/
public @Nullable Collection<String> getCollectedBaseClassNames(String teamName) {
return this.baseClassNamesByTeam.remove(teamName);
}
/**
* Get the names of all adapted base classes encountered while reading the byte code attributes.
* (Destructive read).
*/
public Collection<String> getCollectedBaseClassNames() {
try {
return this.allBaseClassNames;
} finally {
this.allBaseClassNames = new ArrayList<String>();
}
}
/**
* Get the names of all member roles encountered while reading the byte code attributes.
* (Destructive read).
*/
public Collection<String> getCollectedRoleClassNames() {
return this.roleClassNames;
}
/*
* Recurse into member types scanning OT attributes.
*/
void readMemberTypeAttributes(Bundle bundle,
String className,
DelegatingTransformer transformer)
{
List<String> roles = CallinBindingManager.getRolePerTeam(className);
if (roles != null) {
for (@NonNull String roleName: roles) {
log(IStatus.OK, "scanning role "+roleName);
try {
this.roleClassNames.add(roleName);
readOTAttributes(bundle, roleName, transformer);
} catch (Throwable t) {
log(t, "Failed to read OT-Attributes of role "+roleName);
}
readMemberTypeAttributes(bundle, roleName, transformer);
}
}
}
};