/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * 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 the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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.apereo.portal.layout.dao.jpa; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import javax.persistence.Cacheable; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.MapKey; import javax.persistence.OneToMany; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Version; import org.apache.commons.lang.Validate; import org.apereo.portal.dao.usertype.FunctionalNameType; import org.apereo.portal.layout.om.ILayoutAttributeDescriptor; import org.apereo.portal.layout.om.IOutputPropertyDescriptor; import org.apereo.portal.layout.om.IStylesheetData; import org.apereo.portal.layout.om.IStylesheetDescriptor; import org.apereo.portal.layout.om.IStylesheetParameterDescriptor; import org.apereo.portal.layout.om.IStylesheetUserPreferences; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; import org.hibernate.annotations.NaturalId; import org.hibernate.annotations.NaturalIdCache; import org.hibernate.annotations.Type; /** * JPA implementation of stylesheet descriptor data * */ @Entity @Table(name = "UP_SS_DESC") @SequenceGenerator(name = "UP_SS_DESC_GEN", sequenceName = "UP_SS_DESC_SEQ", allocationSize = 5) @TableGenerator(name = "UP_SS_DESC_GEN", pkColumnValue = "UP_SS_DESC", allocationSize = 5) @NaturalIdCache(region = "org.apereo.portal.layout.dao.jpa.StylesheetDescriptorImpl-NaturalId") @Cacheable @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) public class StylesheetDescriptorImpl implements IStylesheetDescriptor { @Id @GeneratedValue(generator = "UP_SS_DESC_GEN") @Column(name = "SS_DESC_ID") private final long id; @Version @Column(name = "ENTITY_VERSION") private final long entityVersion; //Hidden reference to the child stylesheet user preferences, used to allow cascading deletes where when a stylesheet descriptor is deleted all associated preferences are also deleted //MUST BE LAZY FETCH, this set should never actually be populated at runtime or performance will be TERRIBLE @SuppressWarnings("unused") @OneToMany( mappedBy = "stylesheetDescriptor", targetEntity = StylesheetUserPreferencesImpl.class, cascade = {CascadeType.ALL}, fetch = FetchType.LAZY, orphanRemoval = true ) private Set<IStylesheetUserPreferences> stylesheetUserPreferences = null; @NaturalId @Column(name = "SS_NAME", length = 100, nullable = false) @Type(type = "fname") private final String name; @Column(name = "URL_SYNTAX_HELPER_NAME", length = 100) private String urlNodeSyntaxHelperName; @Column(name = "DESCRIPTION", length = 2000) private String description; @Column(name = "STYLESHEET", length = 2000, nullable = false) private String stylesheetResource; @OneToMany( targetEntity = OutputPropertyDescriptorImpl.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true ) @JoinColumn(name = "SS_DESC_ID", nullable = false) @MapKey(name = "name") @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Fetch(FetchMode.JOIN) private Map<String, IOutputPropertyDescriptor> outputProperties = new LinkedHashMap<String, IOutputPropertyDescriptor>(0); @OneToMany( targetEntity = StylesheetParameterDescriptorImpl.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true ) @MapKey(name = "name") @JoinColumn(name = "SS_DESC_ID", nullable = false) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Fetch(FetchMode.JOIN) private Map<String, IStylesheetParameterDescriptor> stylesheetParameters = new LinkedHashMap<String, IStylesheetParameterDescriptor>(0); @OneToMany( targetEntity = LayoutAttributeDescriptorImpl.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true ) @MapKey(name = "name") @JoinColumn(name = "SS_DESC_ID", nullable = false) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Fetch(FetchMode.JOIN) private Map<String, ILayoutAttributeDescriptor> layoutAttributes = new LinkedHashMap<String, ILayoutAttributeDescriptor>(0); //Required for Hibernate reflection @SuppressWarnings("unused") private StylesheetDescriptorImpl() { this.id = -1; this.entityVersion = -1; this.name = null; } StylesheetDescriptorImpl(String name, String stylesheetResource) { FunctionalNameType.validate(name); this.id = -1; this.entityVersion = -1; this.name = name; this.setStylesheetResource(stylesheetResource); } @Override public String getDataId() { return this.getName(); } @Override public String getDataTitle() { return this.getName(); } @Override public String getDataDescription() { return this.getDescription(); } /* (non-Javadoc) * @see org.apereo.portal.layout.om.IStylesheetDescriptor#getId() */ @Override public long getId() { return id; } /* (non-Javadoc) * @see org.apereo.portal.layout.om.IStylesheetDescriptor#getName() */ @Override public String getName() { return this.name; } /* (non-Javadoc) * @see org.apereo.portal.layout.om.IStylesheetDescriptor#setDescription(java.lang.String) */ @Override public void setDescription(String description) { this.description = description; } /* (non-Javadoc) * @see org.apereo.portal.layout.om.IStylesheetDescriptor#getDescription() */ @Override public String getDescription() { return this.description; } /* (non-Javadoc) * @see org.apereo.portal.layout.om.IStylesheetDescriptor#setStylesheetResource(java.lang.String) */ @Override public void setStylesheetResource(String stylesheetResource) { Validate.notEmpty(stylesheetResource); this.stylesheetResource = stylesheetResource; } /* (non-Javadoc) * @see org.apereo.portal.layout.om.IStylesheetDescriptor#getStylesheetResource() */ @Override public String getStylesheetResource() { return this.stylesheetResource; } @Override public void setUrlNodeSyntaxHelperName(String urlNodeSyntaxHelperName) { this.urlNodeSyntaxHelperName = urlNodeSyntaxHelperName; } @Override public String getUrlNodeSyntaxHelperName() { return this.urlNodeSyntaxHelperName; } @Override public Collection<IOutputPropertyDescriptor> getOutputPropertyDescriptors() { return Collections.unmodifiableCollection(this.outputProperties.values()); } @Override public void setOutputPropertyDescriptors( Collection<IOutputPropertyDescriptor> outputPropertyDescriptors) { setMap( this.outputProperties, outputPropertyDescriptors, StylesheetDataUpdater.<IOutputPropertyDescriptor>getInstance()); } @Override public IOutputPropertyDescriptor getOutputPropertyDescriptor(String name) { Validate.notEmpty(name, "name cannot be null"); return this.outputProperties.get(name); } @Override public Collection<IStylesheetParameterDescriptor> getStylesheetParameterDescriptors() { return Collections.unmodifiableCollection(this.stylesheetParameters.values()); } @Override public void setStylesheetParameterDescriptors( Collection<IStylesheetParameterDescriptor> stylesheetParameterDescriptors) { setMap( this.stylesheetParameters, stylesheetParameterDescriptors, StylesheetDataUpdater.<IStylesheetParameterDescriptor>getInstance()); } @Override public IStylesheetParameterDescriptor getStylesheetParameterDescriptor(String name) { Validate.notEmpty(name, "name cannot be null"); return this.stylesheetParameters.get(name); } @Override public Collection<ILayoutAttributeDescriptor> getLayoutAttributeDescriptors() { return Collections.unmodifiableCollection(this.layoutAttributes.values()); } @Override public void setLayoutAttributeDescriptors( Collection<ILayoutAttributeDescriptor> layoutAttributeDescriptors) { setMap( this.layoutAttributes, layoutAttributeDescriptors, LAYOUT_ATTRIBUTE_DESCRIPTOR_UPDATER); } @Override public ILayoutAttributeDescriptor getLayoutAttributeDescriptor(String name) { Validate.notEmpty(name, "name cannot be null"); return this.layoutAttributes.get(name); } protected <T extends IStylesheetData> void setMap( Map<String, T> dataMap, Collection<T> dataCollection, Updater<T> updater) { final Set<String> oldDataKeys = new HashSet<String>(dataMap.keySet()); for (T newData : dataCollection) { final String name = newData.getName(); oldDataKeys.remove(name); final T oldData = dataMap.get(name); if (oldData != null) { newData = updater.update(oldData, newData); } dataMap.put(name, newData); } //Remove all old data entries that were not updated dataMap.keySet().remove(oldDataKeys); } private interface Updater<V> { public V update(V existingObject, V newObject); } private static class StylesheetDataUpdater<V extends IStylesheetData> implements Updater<V> { private static final StylesheetDataUpdater<IStylesheetData> INSTANCE = new StylesheetDataUpdater<IStylesheetData>(); @SuppressWarnings("unchecked") public static <V extends IStylesheetData> StylesheetDataUpdater<V> getInstance() { return (StylesheetDataUpdater<V>) INSTANCE; } @Override public V update(V existingObject, V newObject) { existingObject.setDefaultValue(newObject.getDefaultValue()); existingObject.setDescription(newObject.getDefaultValue()); existingObject.setScope(newObject.getScope()); return existingObject; } } private static final StylesheetDataUpdater<ILayoutAttributeDescriptor> LAYOUT_ATTRIBUTE_DESCRIPTOR_UPDATER = new StylesheetDataUpdater<ILayoutAttributeDescriptor>() { @Override public ILayoutAttributeDescriptor update( ILayoutAttributeDescriptor existingObject, ILayoutAttributeDescriptor newObject) { existingObject.setTargetElementNames(newObject.getTargetElementNames()); return super.update(existingObject, newObject); } }; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; StylesheetDescriptorImpl other = (StylesheetDescriptorImpl) obj; if (this.name == null) { if (other.name != null) return false; } else if (!this.name.equals(other.name)) return false; return true; } @Override public String toString() { return "StylesheetDescriptorImpl [id=" + this.id + ", entityVersion=" + this.entityVersion + ", name=" + this.name + ", urlNodeSyntaxHelperName=" + this.urlNodeSyntaxHelperName + ", description=" + this.description + ", stylesheetResource=" + this.stylesheetResource + "]"; } }