package com.vistatec.ocelot.plugins; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vistatec.ocelot.events.DisplayLeftComponentEvent; import com.vistatec.ocelot.events.EnrichingStartedStoppedEvent; import com.vistatec.ocelot.events.ItsDocStatsRecalculateEvent; import com.vistatec.ocelot.events.RefreshSegmentView; import com.vistatec.ocelot.events.api.OcelotEventQueue; import com.vistatec.ocelot.its.model.EnrichmentMetaData; import com.vistatec.ocelot.plugins.exception.FremeEnrichmentException; import com.vistatec.ocelot.plugins.exception.UnknownServiceException; import com.vistatec.ocelot.segment.model.BaseSegmentVariant; import com.vistatec.ocelot.segment.model.OcelotSegment; import com.vistatec.ocelot.segment.model.enrichment.Enrichment; import com.vistatec.ocelot.xliff.freme.EnrichmentConverter; /** * Class managing calls to the FREME Plugin. It provides a pool of threads * invoking FREME services for Ocelot fragments. */ public class FremePluginManager { /** The logger for this class. */ private final Logger logger = LoggerFactory.getLogger(FremePluginManager.class); /** Ideal segments number per call. */ private static final int SEGNUM_PER_CALL = 20; /** Maximum number of threads allowed in the pool. */ private static final int MAX_THREAD_NUM = 10; public static final int OVERRIDE_ENRICHMENTS = 0; public static final int MERGE_ENRICHMENTS = 1; /** The Ocelot event queue. */ private OcelotEventQueue eventQueue; /** The executor service. */ private ExecutorService executor; /** List of segments currently opened in Ocelot. */ private List<OcelotSegment> segments; /** States if the FREME plugin is enriching. */ private boolean enriching; /** The FREME menu to be displayed in the Ocelot menu bar */ private JMenu fremeMenu; /** The FREME menu item to be displayed in segment view context menu. */ private JMenuItem fremeMenuItem; /** * Constructor. * * @param eventQueue * the event queue. */ public FremePluginManager(final OcelotEventQueue eventQueue) { this.eventQueue = eventQueue; createExecutor(); } private void createExecutor() { executor = Executors.newFixedThreadPool(MAX_THREAD_NUM); } /** * Sets the segments list. * * @param segments * the segments list. */ public void setSegments(List<OcelotSegment> segments) { this.segments = segments; } /** * Enriches the segments opened in Ocelot by invoking the FREME plugin. * * @param fremePlugin * the FREME plugin */ public void enrich(FremePlugin fremePlugin, int action) { if (segments != null) { if (action == OVERRIDE_ENRICHMENTS && existEnrichments()) { resetSegments(); } logger.info("Enriching Ocelot segments..."); List<VariantWrapper> fragments = getFragments(segments); Collections.sort(fragments, new FragmentsComparator()); List<VariantWrapper> fragmentsToDelete = new ArrayList<VariantWrapper>(); int threadNum = findThreadNum(fragments.size()); VariantWrapper[][] fragmentsArrays = new VariantWrapper[threadNum][(int) Math .ceil((double) fragments.size() / threadNum)]; int j = 0; for (int i = 0; i < fragments.size(); i = i + threadNum) { for (int arrayIdx = 0; arrayIdx < threadNum; arrayIdx++) { if (i + arrayIdx < fragments.size()) { fragmentsArrays[arrayIdx][j] = fragments.get(i + arrayIdx); fragmentsToDelete.add(fragments.get(i + arrayIdx)); } } j++; } System.out.println("Fragments size = " + fragments.size()); int count = 0; for (int i = 0; i < fragmentsArrays.length; i++) { count += fragmentsArrays[i].length; } System.out.println("Total fragments for FREME = " + count); fragments.removeAll(fragmentsToDelete); System.out.println(fragments.size()); eventQueue.post(new EnrichingStartedStoppedEvent( EnrichingStartedStoppedEvent.STARTED)); addTasksToExecutor(fragmentsArrays, fremePlugin); WaitingThread waitingThread = new WaitingThread(executor, segments, eventQueue); waitingThread.start(); } } /** * Resets all segments enrichments. */ private void resetSegments() { logger.debug("Resetting segments before enrichment."); for (OcelotSegment segment : segments) { if (segment.getSource() instanceof BaseSegmentVariant) { resetVariant(segment, (BaseSegmentVariant) segment.getSource(), false); } if (segment.getTarget() != null && segment.getTarget() instanceof BaseSegmentVariant) { resetVariant(segment, (BaseSegmentVariant) segment.getTarget(), true); } } eventQueue.post(new RefreshSegmentView(-1)); eventQueue.post(new ItsDocStatsRecalculateEvent(segments)); } private void resetVariant(OcelotSegment segment, BaseSegmentVariant variant, boolean target) { EnrichmentConverter.removeEnrichmentMetaData(segment, variant, target); variant.clearEnrichments(); } /** * Enriches a variant of an existing segment. * * @param fremePlugin * the freme plugin * @param variant * the variant to be enriched * @param segNumber * the segment number. */ public void enrich(FremePlugin fremePlugin, BaseSegmentVariant variant, int segNumber, boolean target, int action) { if (action == OVERRIDE_ENRICHMENTS) { resetVariant(getSegmentBySegNum(segNumber), variant, target); } else { variant.setEnriched(false); } eventQueue.post(new RefreshSegmentView(segNumber)); logger.info("Enriching variant for segment {}...", segNumber); VariantWrapper wrapper = new VariantWrapper(variant, variant.getDisplayText(), segNumber, target); if (wrapper.getText() != null && !wrapper.getText().isEmpty()) { eventQueue.post(new EnrichingStartedStoppedEvent( EnrichingStartedStoppedEvent.STARTED)); addTasksToExecutor(new VariantWrapper[][] { { wrapper } }, fremePlugin); WaitingThread waitingThread = new WaitingThread(executor, segments, eventQueue); waitingThread.start(); } } private OcelotSegment getSegmentBySegNum(int segNumber) { OcelotSegment segment = null; int i = 0; while (i < segments.size() && segment == null) { if (segments.get(i).getSegmentNumber() == segNumber) { segment = segments.get(i); } i++; } return segment; } /** * Finds the optimal threads number depending on the number of segments to * be enriched. * * @param segmentsSize * the number of segments * @return the optimal threads number. */ private int findThreadNum(int segmentsSize) { int threadNum = 2; boolean found = false; while (!found) { if (segmentsSize / threadNum <= SEGNUM_PER_CALL) { found = true; } else { threadNum++; } } return threadNum; } /** * Gets the list of fragments to be enriched retrieved by the list of * segments. * * @param segments * the list of Ocelot segments. * @return the list of fragments */ private List<VariantWrapper> getFragments(List<OcelotSegment> segments) { List<VariantWrapper> fragments = new ArrayList<VariantWrapper>(); if (segments != null) { String text = null; for (OcelotSegment segment : segments) { if (segment.getSource() != null /* * && !((BaseSegmentVariant) segment.getSource()) .isEnriched() */) { text = segment.getSource().getDisplayText(); if (text != null && !text.isEmpty()) { fragments.add(new VariantWrapper( (BaseSegmentVariant) segment.getSource(), text, segment.getSegmentNumber(), false)); } } if (segment.getTarget() != null /* * && !((BaseSegmentVariant) segment.getTarget()) .isEnriched() */) { text = segment.getTarget().getDisplayText(); if (text != null && !text.isEmpty()) { fragments.add(new VariantWrapper( (BaseSegmentVariant) segment.getTarget(), text, segment.getSegmentNumber(), true)); } } } } return fragments; } ExecutorService getexecutor() { return executor; } List<OcelotSegment> getSegments() { return segments; } OcelotEventQueue getEventQueue() { return eventQueue; } public boolean existEnrichments() { boolean exist = false; Iterator<OcelotSegment> segIterator = segments.iterator(); OcelotSegment segment = null; BaseSegmentVariant source = null; BaseSegmentVariant target = null; while (segIterator.hasNext() && !exist) { segment = segIterator.next(); if (segment.getSource() instanceof BaseSegmentVariant) { source = (BaseSegmentVariant) segment.getSource(); } if (segment.getTarget() != null && segment.getTarget() instanceof BaseSegmentVariant) { target = (BaseSegmentVariant) segment.getTarget(); } exist = (source != null && source.isEnriched()) || (target != null && target.isEnriched()); } return exist; } public void setEnriching(boolean enriching) { this.enriching = enriching; } public boolean isEnriching() { return enriching; } private synchronized void addTasksToExecutor( VariantWrapper[][] fragmentsArrays, FremePlugin fremePlugin) { if (executor.isShutdown()) { createExecutor(); } for (int i = 0; i < fragmentsArrays.length; i++) { executor.execute(new FremeEnricher(fragmentsArrays[i], fremePlugin, eventQueue, segments)); } } public JMenu getFremeMenu(final FremePlugin fremePlugin) { if (fremeMenu == null) { ActionListener listener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (fremePlugin != null) { FremeMenuItem menuItem = (FremeMenuItem) e.getSource(); if (menuItem.getMenuType() == FremeMenu.CONFIG_MENU) { Window containerWindow = SwingUtilities .getWindowAncestor(fremeMenu); fremePlugin.configureServiceChain(containerWindow); } else if (menuItem.getMenuType() == FremeMenu.FILTER_MENU) { eventQueue.post(new DisplayLeftComponentEvent( fremePlugin.getCategoryFilterPanel())); } else if (menuItem.getMenuType() == FremeMenu.ENRICH_MENU) { if (existEnrichments()) { Window containerWindow = SwingUtilities .getWindowAncestor(fremeMenu); int option = FremeEnrichmentOptions .showConfirmDialog(containerWindow); if (option == FremeEnrichmentOptions.DELETE_OPTION) { enrich(fremePlugin, FremePluginManager.OVERRIDE_ENRICHMENTS); } else if (option == FremeEnrichmentOptions.MERGE_OPTION) { enrich(fremePlugin, FremePluginManager.MERGE_ENRICHMENTS); } } else { enrich(fremePlugin, FremePluginManager.OVERRIDE_ENRICHMENTS); } } } } }; ItemListener itemListener = new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { FremeEServiceMenuItem menuItem = (FremeEServiceMenuItem) e .getItemSelectable(); try { if (fremePlugin != null) { if (e.getStateChange() == ItemEvent.SELECTED) { fremePlugin.turnOnService(menuItem .getServiceType()); } else { fremePlugin.turnOffService(menuItem .getServiceType()); } } } catch (UnknownServiceException exc) { logger.trace( "Error while turning on/off the service with type: " + menuItem.getServiceType(), exc); JOptionPane .showMessageDialog( null, "An error has occurred while turning on/off the service.", "Freme e-Service", JOptionPane.ERROR_MESSAGE); } } }; fremeMenu = new FremeMenu(itemListener, listener); } // boolean enableMenu = false; // for (Entry<FremePlugin, Boolean> fremePlugin : fremePlugins // .entrySet()) { // if (fremePlugin.getValue()) { // enableMenu = true; // break; // } // } // fremeMenu.setEnabled(enableMenu); return fremeMenu; } public void setFremeMenuEnabled(boolean enabled) { if (fremeMenu != null) { fremeMenu.setEnabled(enabled); } } public synchronized List<JMenuItem> getSegmentContextMenuItems( final FremePlugin fremePlugin, final OcelotSegment segment, final BaseSegmentVariant variant, final boolean target) { List<JMenuItem> items = new ArrayList<JMenuItem>(); fremeMenuItem = new JMenuItem("Enrich"); ActionListener listener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (existEnrichments()) { Window containerWindow = SwingUtilities .getWindowAncestor(fremeMenuItem); int option = FremeEnrichmentOptions .showConfirmDialog(containerWindow); if (option == FremeEnrichmentOptions.DELETE_OPTION) { enrich(fremePlugin, variant, segment.getSegmentNumber(), target, FremePluginManager.OVERRIDE_ENRICHMENTS); } else if (option == FremeEnrichmentOptions.MERGE_OPTION) { enrich(fremePlugin, variant, segment.getSegmentNumber(), target, FremePluginManager.MERGE_ENRICHMENTS); } } else { enrich(fremePlugin, variant, segment.getSegmentNumber(), target, FremePluginManager.OVERRIDE_ENRICHMENTS); } } }; fremeMenuItem.addActionListener(listener); if (enriching) { fremeMenuItem.setEnabled(false); } items.add(fremeMenuItem); return items; } public synchronized void setContextMenuItemEnabled(boolean enabled) { if (fremeMenuItem != null) { fremeMenuItem.setEnabled(enabled); } } } class WaitingThread extends Thread { private ExecutorService executor; private List<OcelotSegment> segments; private OcelotEventQueue eventQueue; public WaitingThread(ExecutorService executor, List<OcelotSegment> segments, OcelotEventQueue eventQueue) { this.eventQueue = eventQueue; this.executor = executor; this.segments = segments; } @Override public void run() { synchronized (executor) { executor.shutdown(); } try { executor.awaitTermination(5, TimeUnit.MINUTES); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } eventQueue.post(new ItsDocStatsRecalculateEvent(segments)); eventQueue.post(new EnrichingStartedStoppedEvent( EnrichingStartedStoppedEvent.STOPPED)); System.out.println("-----------------------------------"); System.out.println("---------ENRICHED SEGMENTS---------"); System.out.println("-----------------------------------"); int count = 0; for (OcelotSegment segment : segments) { if (((BaseSegmentVariant) segment.getSource()).isEnriched() && ((BaseSegmentVariant) segment.getTarget()).isEnriched()) { System.out.println(segment.getSegmentNumber()); count++; } } System.out.println("TOTAL: " + count); } } /** * Comparator for fragments. A fragment is smaller than another one, if its text * is shorter than the other's text. */ class FragmentsComparator implements Comparator<VariantWrapper> { @Override public int compare(VariantWrapper o1, VariantWrapper o2) { int retValue = 0; if (o1.getText().length() > o2.getText().length()) { retValue = 1; } else if (o1.getText().length() < o2.getText().length()) { retValue = -1; } return retValue; } } /** * Wrapper class for variant objects. */ class VariantWrapper { /** The variant. */ private BaseSegmentVariant variant; /** The text contained into the variant. */ private String text; /** The owner segment number. */ private int segNumber; private boolean target; /** * Constructor. * * @param variant * the variant * @param text * the text * @param segNumber * the segment number */ public VariantWrapper(BaseSegmentVariant variant, String text, int segNumber, boolean target) { this.variant = variant; this.text = text; this.segNumber = segNumber; this.target = target; } /** * Gets the variant. * * @return the variant. */ public BaseSegmentVariant getVariant() { return variant; } /** * Gets the text. * * @return the text. */ public String getText() { return text; } /** * Gets the segment number. * * @return the segment number. */ public int getSegNumber() { return segNumber; } public boolean isTarget() { return target; } } /** * Runnable class performing the enrichment of an array of variants. */ class FremeEnricher implements Runnable { /** The logger for this class. */ private final Logger logger = LoggerFactory.getLogger(FremeEnricher.class); /** The array of variants. */ private VariantWrapper[] variants; /** The FREME plugin. */ private FremePlugin fremePlugin; /** The event queue. */ private OcelotEventQueue eventQueue; private List<OcelotSegment> segments; /** * Constructor. * * @param variants * the array of variants to be enriched. * @param fremePlugin * the FREME plugin * @param eventQueue * the event queue */ public FremeEnricher(final VariantWrapper[] variants, FremePlugin fremePlugin, OcelotEventQueue eventQueue, List<OcelotSegment> segments) { this.variants = variants; this.fremePlugin = fremePlugin; this.eventQueue = eventQueue; this.segments = segments; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ @Override public void run() { logger.debug("Enriching {} variants", variants.length); for (VariantWrapper frag : variants) { if (frag != null) { try { List<Enrichment> enrichments = null; String sourceTarget = null; frag.getVariant().setSentToFreme(true); if (frag.isTarget()) { enrichments = fremePlugin.enrichTargetContent(frag .getText()); sourceTarget = EnrichmentMetaData.TARGET; } else { enrichments = fremePlugin.enrichSourceContent(frag .getText()); sourceTarget = EnrichmentMetaData.SOURCE; } frag.getVariant().setEnrichments( new HashSet<Enrichment>(enrichments)); frag.getVariant().setEnriched(true); OcelotSegment segment = findSegmentBySegNumber(frag .getSegNumber()); if (segment != null) { EnrichmentConverter.convertEnrichment2ITSMetaData( segment, frag.getVariant(), sourceTarget); } } catch (FremeEnrichmentException e) { logger.error("Error while enriching the variant " + frag.getVariant().getDisplayText(), e); } finally { eventQueue .post(new RefreshSegmentView(frag.getSegNumber())); } } } } /** * Finds the segment having the specified segment number. * * @param segNum * the segment number. * @return the Ocelot segment. */ private OcelotSegment findSegmentBySegNumber(int segNum) { OcelotSegment segment = null; Iterator<OcelotSegment> segmIt = segments.iterator(); OcelotSegment currSegm = null; while (segmIt.hasNext() && segment == null) { currSegm = segmIt.next(); if (currSegm.getSegmentNumber() == segNum) { segment = currSegm; } } return segment; } }