/* * Copyright 2003-2013 JetBrains s.r.o. * * 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 jetbrains.mps.editor.runtime.style; import jetbrains.mps.editor.runtime.style.StyleAttributeMap.DiscardValue; import jetbrains.mps.openapi.editor.style.Style; import jetbrains.mps.openapi.editor.style.StyleAttribute; import jetbrains.mps.openapi.editor.style.StyleChangeEvent; import jetbrains.mps.openapi.editor.style.StyleListener; import jetbrains.mps.util.EqualUtil; import jetbrains.mps.util.containers.EmptyIterator; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * User: shatalin * Date: 1/11/13 */ public class StyleImpl implements Style { private static final Logger LOG = LogManager.getLogger(StyleImpl.class); private Style myParent; private List<Style> myChildren = null; private List<StyleListener> myStyleListeners = null; private TopLevelStyleMap myAttributes = new TopLevelStyleMap(); private TopLevelStyleMap myCachedAttributes = new TopLevelStyleMap(); @Override public void putAll(@NotNull Style style) { putAll(style, 0); } @Override public void putAll(@NotNull Style style, int selfPriority) { Set<StyleAttribute> addedSimple = new StyleAttributeSet(); Set<StyleAttribute> addedNotSimple = new StyleAttributeSet(); for (StyleAttribute<Object> attribute : style.getSpecifiedAttributes()) { Collection<IntPair<Object>> putAttributes = style.getAll(attribute); if (putAttributes != null) { int attributePointer = myAttributes.search(attribute.getIndex()); for (IntPair<Object> value : putAttributes) { attributePointer = myAttributes.setValue(attribute, attributePointer, Math.max(value.index, selfPriority), value.value == null ? DiscardValue.getInstance() : value.value); } } if (StyleAttributes.isSimple(attribute)) { addedSimple.add(attribute); } else { addedNotSimple.add(attribute); } } updateCache(addedNotSimple); fireStyleChanged(new StyleChangeEvent(this, addedSimple)); } @Override public void removeAll(@NotNull Style style) { Set<StyleAttribute> addedSimple = new StyleAttributeSet(); Set<StyleAttribute> addedNotSimple = new StyleAttributeSet(); for (StyleAttribute<Object> attribute : style.getSpecifiedAttributes()) { Collection<IntPair<Object>> putAttributes = style.getAll(attribute); if (putAttributes != null) { int attributePointer = myAttributes.search(attribute.getIndex()); for (IntPair<Object> value : putAttributes) { attributePointer = myAttributes.setValue(attribute, attributePointer, value.index, DiscardValue.getInstance()); } } if (StyleAttributes.isSimple(attribute)) { addedSimple.add(attribute); } else { addedNotSimple.add(attribute); } } updateCache(addedNotSimple); fireStyleChanged(new StyleChangeEvent(this, addedSimple)); } @Override public <T> void set(StyleAttribute<T> attribute, int priority, T value) { myAttributes.setValue(attribute, priority, value); Set<StyleAttribute> attributeSet = Collections.<StyleAttribute>singleton(attribute); if (StyleAttributes.isSimple(attribute)) { fireStyleChanged(new StyleChangeEvent(this, attributeSet)); } else { updateCache(attributeSet); } } @Override public <T> void set(StyleAttribute<T> attribute, T value) { set(attribute, 0, value); } @Override public <T> int getHighestPriority(StyleAttribute<T> attribute) { int cachedAttributePointer = myCachedAttributes.search(attribute.getIndex()); if (TopLevelStyleMap.isEmpty(cachedAttributePointer)) { return -1; } else { return myCachedAttributes.getTopPair(attribute, cachedAttributePointer).index; } } @Override public <T> T get(StyleAttribute<T> attribute) { if (StyleAttributes.isSimple(attribute)) { IntPair<T> topPair = myAttributes.getTopPair(attribute); return topPair == null ? attribute.combine(null, null) : topPair.value; } else { IntPair<T> topPair = myCachedAttributes.getTopPair(attribute); return topPair == null ? attribute.combine(null, null) : topPair.value; } } @Override @Nullable public <T> Collection<IntPair<T>> getAll(StyleAttribute<T> attribute) { int attributePointer = myAttributes.search(attribute.getIndex()); return TopLevelStyleMap.isEmpty(attributePointer) ? null : myAttributes.getDiscardNullReplaced(attribute, attributePointer); } @Override @Nullable public <T> Collection<IntPair<T>> getAllCached(StyleAttribute<T> attribute) { if (StyleAttributes.isSimple(attribute)) { int attributePointer = myAttributes.search(attribute.getIndex()); return TopLevelStyleMap.isEmpty(attributePointer) ? null : (Collection) myAttributes.getAll(attribute, attributePointer); } else { int cachedAttributePointer = myCachedAttributes.search(attribute.getIndex()); return TopLevelStyleMap.isEmpty(cachedAttributePointer) ? null : (Collection) myCachedAttributes.getAll(attribute, cachedAttributePointer); } } @Override public <T> boolean isSpecified(StyleAttribute<T> attribute) { return !TopLevelStyleMap.isEmpty(myAttributes.search(attribute.getIndex())); } @Override public Set<StyleAttribute> getSpecifiedAttributes() { StyleAttributeSet res = new StyleAttributeSet(); for (int attributeIndex : myAttributes.getIndexes()) { res.add(attributeIndex); } return res; } @Override public void addListener(StyleListener l) { if (myStyleListeners == null) { myStyleListeners = new ArrayList<StyleListener>(1); } myStyleListeners.add(l); } @Override public void removeListener(StyleListener l) { if (myStyleListeners == null) { return; } myStyleListeners.remove(l); if (myStyleListeners.isEmpty()) { myStyleListeners = null; } } private void fireStyleChanged(StyleChangeEvent e) { if (myStyleListeners == null) { return; } for (StyleListener l : myStyleListeners) { try { l.styleChanged(e); } catch (Throwable t) { LOG.error(null, t); } } } @Override public void add(Style child) { if (myChildren == null) { myChildren = new LinkedList<Style>(); } myChildren.add(child); child.setParent(this, getNonDefaultValuedAttributes()); } @Override public void remove(Style child) { myChildren.remove(child); if (myChildren.size() == 0) { myChildren = null; } child.setParent(null, getNonDefaultValuedAttributes()); } @Override public void setParent(Style parent, Collection<StyleAttribute> inheritedAttributes) { myParent = parent; updateCache(inheritedAttributes); } private Set<StyleAttribute> getNonDefaultValuedAttributes() { StyleAttributeSet result = new StyleAttributeSet(); for (int attributeIndex : myCachedAttributes.getIndexes()) { result.add(attributeIndex); } return result; } private Style getParentStyle() { return myParent; } private void updateCache(Collection<StyleAttribute> attributes) { if (attributes.isEmpty()) { return; } Set<StyleAttribute> changedAttributes = new StyleAttributeSet(); for (StyleAttribute<Object> attribute : attributes) { assert !StyleAttributes.isSimple(attribute); int attributePointer = myAttributes.search(attribute.getIndex()); int cachedAttributePointer = myCachedAttributes.search(attribute.getIndex()); Collection<IntPair<Object>> parentValues = getParentStyle() == null ? null : getParentStyle().getAllCached(attribute); Collection<IntPair<Object>> currentValues = TopLevelStyleMap.isEmpty(attributePointer) ? null : myAttributes.getAll(attribute, attributePointer); Collection<IntPair<Object>> oldValues = TopLevelStyleMap.isEmpty(cachedAttributePointer) ? null : myCachedAttributes.getAll(attribute, cachedAttributePointer); Iterator<IntPair<Object>> parentIterator = parentValues == null ? new EmptyIterator<IntPair<Object>>() : parentValues.iterator(); Iterator<IntPair<Object>> currentIterator = currentValues == null ? new EmptyIterator<IntPair<Object>>() : currentValues.iterator(); IntPair<Object> parentValue; IntPair<Object> currentValue; parentValue = parentIterator.hasNext() ? parentIterator.next() : null; currentValue = currentIterator.hasNext() ? currentIterator.next() : null; StyleAttributeMap<Object> newValues = new StyleAttributeMap<Object>(); while (parentValue != null || currentValue != null ) { if (currentValue != null && (parentValue == null || currentValue.index < parentValue.index)) { if (!(currentValue.value instanceof DiscardValue)) { newValues.setValue(currentValue.index, attribute.combine(null, currentValue.value)); } currentValue = currentIterator.hasNext() ? currentIterator.next() : null; } else if (currentValue == null || parentValue.index < currentValue.index) { newValues.setValue(parentValue.index, attribute.combine(parentValue.value, null)); parentValue = parentIterator.hasNext() ? parentIterator.next() : null; } else { if (!(currentValue.value instanceof DiscardValue)) { newValues.setValue(currentValue.index, attribute.combine(parentValue.value, currentValue.value)); } currentValue = currentIterator.hasNext() ? currentIterator.next() : null; parentValue = parentIterator.hasNext() ? parentIterator.next() : null; } } Iterator<IntPair<Object>> oldIterator = oldValues == null ? new EmptyIterator<IntPair<Object>>() : oldValues.iterator(); Iterator<IntPair<Object>> newIterator = newValues.getAll().iterator(); while (oldIterator.hasNext() || newIterator.hasNext()) { if (newIterator.hasNext() ^ oldIterator.hasNext()) { changedAttributes.add(attribute); break; } IntPair<Object> newValue = newIterator.next(); IntPair<Object> oldValue = oldIterator.next(); if (newValue.index != oldValue.index || !EqualUtil.equals(newValue.value, oldValue.value)) { changedAttributes.add(attribute); break; } } myCachedAttributes.set(attribute.getIndex(), cachedAttributePointer, newValues); } if (!changedAttributes.isEmpty()) { if (myChildren != null) { for (Style style : myChildren) { style.setParent(this, changedAttributes); } } fireStyleChanged(new StyleChangeEvent(this, changedAttributes)); } } }