| /* |
| * 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. |
| */ |
| package org.apache.openjpa.meta; |
| |
| import java.io.Serializable; |
| import java.util.Arrays; |
| |
| import org.apache.openjpa.event.LifecycleCallbacks; |
| import org.apache.openjpa.event.LifecycleEvent; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.util.InternalException; |
| |
| /** |
| * Information about lifecycle events for a managed type. |
| * |
| * @author Steve Kim |
| * @author Abe White |
| */ |
| public class LifecycleMetaData |
| implements Serializable { |
| |
| |
| private static final long serialVersionUID = 1L; |
| public static final int IGNORE_NONE = 0; |
| public static final int IGNORE_HIGH = 2 << 0; |
| public static final int IGNORE_LOW = 2 << 1; |
| |
| private static final LifecycleCallbacks[] EMPTY_CALLBACKS = |
| new LifecycleCallbacks[0]; |
| private static final Localizer _loc = Localizer.forPackage |
| (LifecycleMetaData.class); |
| |
| private final ClassMetaData _meta; |
| private LifecycleCallbacks[][] _declared = null; |
| private LifecycleCallbacks[][] _super = null; |
| private LifecycleCallbacks[][] _all = null; |
| private int[] _high = null; |
| private int[] _superHigh = null; |
| private boolean _resolved = false; |
| private boolean _ignoreSystem = false; |
| private int _ignoreSups = 0; |
| private boolean _activated = false; |
| |
| /** |
| * Construct with owning metadata. |
| */ |
| LifecycleMetaData(ClassMetaData meta) { |
| _meta = meta; |
| } |
| |
| /** |
| * Whether the LifeCycleMetaData has had any callbacks or listeners registered. Used |
| * for a quick test to determine whether to attempt to fire any events. |
| * @return boolean |
| */ |
| public boolean is_activated() { |
| return _activated; |
| } |
| |
| /** |
| * Whether to exclude system listeners from events. |
| */ |
| public boolean getIgnoreSystemListeners() { |
| return _ignoreSystem; |
| } |
| |
| /** |
| * Whether to exclude system listeners from events. |
| */ |
| public void setIgnoreSystemListeners(boolean ignore) { |
| _ignoreSystem = ignore; |
| } |
| |
| /** |
| * Whether to exclude superclass callbacks from events. |
| */ |
| public int getIgnoreSuperclassCallbacks() { |
| return _ignoreSups; |
| } |
| |
| /** |
| * Whether to exclude superclass callbacks from events. |
| */ |
| public void setIgnoreSuperclassCallbacks(int ignore) { |
| _ignoreSups = ignore; |
| } |
| |
| /** |
| * Return the declared callbacks for the given event type. |
| */ |
| public LifecycleCallbacks[] getDeclaredCallbacks(int eventType) { |
| return (_declared == null || _declared[eventType] == null) |
| ? EMPTY_CALLBACKS : _declared[eventType]; |
| } |
| |
| /** |
| * Return all callbacks for the given event type, including superclass |
| * callbacks if appropriate. |
| */ |
| public LifecycleCallbacks[] getCallbacks(int eventType) { |
| resolve(); |
| return (_all == null || _all[eventType] == null) |
| ? EMPTY_CALLBACKS : _all[eventType]; |
| } |
| |
| /** |
| * Set the callbacks for the given event type. |
| * |
| * @param highPriority the first N given callbacks are high priority; |
| * high priority callbacks will be returned before |
| * non-high-priority superclass callbacks |
| */ |
| public void setDeclaredCallbacks(int eventType, |
| LifecycleCallbacks[] callbacks, int highPriority) { |
| if (_resolved) |
| throw new InternalException(_loc.get("lifecycle-resolved", |
| _meta, Arrays.asList(callbacks))); |
| |
| if (_declared == null) { |
| _declared = new LifecycleCallbacks |
| [LifecycleEvent.ALL_EVENTS.length][]; |
| _high = new int[LifecycleEvent.ALL_EVENTS.length]; |
| } |
| _declared[eventType] = callbacks; |
| _high[eventType] = highPriority; |
| _activated = true; |
| } |
| |
| /** |
| * Return the callbacks for the non-PC superclass. |
| */ |
| public LifecycleCallbacks[] getNonPCSuperclassCallbacks |
| (int eventType) { |
| return (_super == null || _super[eventType] == null) |
| ? EMPTY_CALLBACKS : _super[eventType]; |
| } |
| |
| /** |
| * Set the callbacks for the given event type for non-persistent |
| * superclass. Note these callbacks will only be used where the |
| * non-persistent superclass is the direct ancestor of the described class. |
| * |
| * @param highPriority the first N given callbacks are high priority; |
| * high priority callbacks will be returned before |
| * non-high-priority superclass callbacks |
| */ |
| public void setNonPCSuperclassCallbacks(int eventType, |
| LifecycleCallbacks[] callbacks, int highPriority) { |
| if (_resolved) |
| throw new InternalException(_loc.get("lifecycle-resolved", |
| _meta, Arrays.asList(callbacks))); |
| |
| if (_super == null) { |
| _super = new LifecycleCallbacks |
| [LifecycleEvent.ALL_EVENTS.length][]; |
| _superHigh = new int[LifecycleEvent.ALL_EVENTS.length]; |
| } |
| _super[eventType] = callbacks; |
| _superHigh[eventType] = highPriority; |
| _activated = true; |
| } |
| |
| /** |
| * Resolve all callbacks. |
| */ |
| void resolve() { |
| if (!_resolved) { |
| _all = combineCallbacks(); |
| _resolved = true; |
| } |
| } |
| |
| /** |
| * Combine our callbacks with superclass callbacks as necessary. |
| * This method has the side effect of manipulating the _high array to |
| * reflect the combined callbacks rather than the declared ones. |
| */ |
| private LifecycleCallbacks[][] combineCallbacks() { |
| if (_ignoreSups == (IGNORE_HIGH | IGNORE_LOW)) |
| return _declared; |
| |
| LifecycleMetaData supMeta = (_meta.getPCSuperclass() == null) ? null |
| : _meta.getPCSuperclassMetaData().getLifecycleMetaData(); |
| if (supMeta == null && _super == null) |
| return _declared; |
| |
| if (supMeta != null) { |
| supMeta.resolve(); |
| if (supMeta._all == null) |
| return _declared; |
| if (_declared == null && _ignoreSups == 0) { |
| _high = supMeta._high; |
| _activated = true; |
| return supMeta._all; |
| } |
| // don't hold strong refs onto redundant info |
| _super = null; |
| _superHigh = null; |
| } |
| |
| LifecycleCallbacks[][] all = new LifecycleCallbacks |
| [LifecycleEvent.ALL_EVENTS.length][]; |
| LifecycleCallbacks[] decs, sups; |
| int supStart, supEnd, supHigh; |
| int count; |
| for (int i = 0; i < all.length; i++) { |
| decs = getDeclaredCallbacks(i); |
| if (supMeta == null) { |
| sups = (_super[i] == null) ? EMPTY_CALLBACKS : _super[i]; |
| supHigh = (_superHigh == null) ? 0 : _superHigh[i]; |
| } else { |
| sups = supMeta.getCallbacks(i); |
| supHigh = (supMeta._high == null) ? 0 : supMeta._high[i]; |
| } |
| supStart = ((_ignoreSups & IGNORE_HIGH) != 0) ? supHigh : 0; |
| supEnd = ((_ignoreSups & IGNORE_LOW) != 0) ? supHigh : sups.length; |
| |
| if (supEnd - supStart == 0) |
| all[i] = decs; |
| else if (decs.length == 0) { |
| if (supEnd - supStart == sups.length) |
| all[i] = sups; |
| else { |
| all[i] = new LifecycleCallbacks[supEnd - supStart]; |
| System.arraycopy(sups, supStart, all[i], 0, all[i].length); |
| } |
| if (_high == null) |
| _high = new int[all.length]; |
| _high[i] = supHigh - supStart; |
| } else { |
| all[i] = |
| new LifecycleCallbacks[decs.length + supEnd - supStart]; |
| count = 0; |
| |
| // add superclass high priority callbacks first |
| if ((_ignoreSups & IGNORE_HIGH) == 0) |
| for (int j = 0; j < supHigh; j++) |
| all[i][count++] = sups[j]; |
| // then our high priority |
| for (int j = 0; j < _high[i]; j++) |
| all[i][count++] = decs[j]; |
| // then superclass low priority |
| if ((_ignoreSups & IGNORE_LOW) == 0) |
| for (int j = supHigh; j < sups.length; j++) |
| all[i][count++] = sups[j]; |
| // then our low priority |
| for (int j = _high[i]; j < decs.length; j++) |
| all[i][count++] = decs[j]; |
| |
| if ((_ignoreSups & IGNORE_HIGH) == 0) |
| _high[i] += supHigh; |
| } |
| } |
| return all; |
| } |
| } |